aws-cis-controls-assessment 1.1.1__tar.gz → 1.1.2__tar.gz
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.
- {aws_cis_controls_assessment-1.1.1/aws_cis_controls_assessment.egg-info → aws_cis_controls_assessment-1.1.2}/PKG-INFO +1 -1
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/__init__.py +1 -1
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/reporters/html_reporter.py +118 -175
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2/aws_cis_controls_assessment.egg-info}/PKG-INFO +1 -1
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/LICENSE +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/MANIFEST.in +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/README.md +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/cli/__init__.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/cli/examples.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/cli/main.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/cli/utils.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/config/__init__.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/config/config_loader.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/config/rules/cis_controls_ig1.yaml +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/config/rules/cis_controls_ig2.yaml +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/config/rules/cis_controls_ig3.yaml +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/__init__.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/base_control.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/ig1/__init__.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/ig1/control_1_1.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/ig1/control_2_2.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/ig1/control_3_3.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/ig1/control_3_4.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/ig1/control_4_1.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/ig1/control_access_keys.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/ig1/control_advanced_security.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/ig1/control_aws_backup_service.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/ig1/control_backup_recovery.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/ig1/control_cloudtrail_logging.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/ig1/control_critical_security.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/ig1/control_data_protection.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/ig1/control_iam_advanced.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/ig1/control_iam_governance.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/ig1/control_iam_policies.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/ig1/control_instance_optimization.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/ig1/control_network_enhancements.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/ig1/control_network_security.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/ig1/control_s3_enhancements.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/ig1/control_s3_security.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/ig1/control_vpc_security.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/ig2/__init__.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/ig2/control_3_10.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/ig2/control_3_11.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/ig2/control_4_5_6_access_configuration.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/ig2/control_5_2.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/ig2/control_8_audit_logging.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/ig2/control_advanced_encryption.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/ig2/control_aws_backup_ig2.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/ig2/control_codebuild_security.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/ig2/control_encryption_rest.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/ig2/control_encryption_transit.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/ig2/control_network_ha.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/ig2/control_remaining_encryption.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/ig2/control_remaining_rules.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/ig2/control_service_logging.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/ig3/__init__.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/ig3/control_12_8.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/ig3/control_13_1.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/ig3/control_3_14.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/controls/ig3/control_7_1.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/core/__init__.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/core/accuracy_validator.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/core/assessment_engine.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/core/audit_trail.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/core/aws_client_factory.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/core/error_handler.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/core/models.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/core/scoring_engine.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/reporters/__init__.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/reporters/base_reporter.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/reporters/csv_reporter.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_assessment/reporters/json_reporter.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_controls_assessment.egg-info/SOURCES.txt +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_controls_assessment.egg-info/dependency_links.txt +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_controls_assessment.egg-info/entry_points.txt +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_controls_assessment.egg-info/requires.txt +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/aws_cis_controls_assessment.egg-info/top_level.txt +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/deprecation-package/aws_cis_assessment_deprecated/__init__.py +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/docs/README.md +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/docs/adding-aws-backup-controls.md +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/docs/assessment-logic.md +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/docs/cli-reference.md +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/docs/config-rule-mappings.md +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/docs/developer-guide.md +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/docs/dual-scoring-implementation.md +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/docs/html-report-improvements.md +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/docs/installation.md +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/docs/scoring-comparison-aws-config.md +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/docs/scoring-methodology.md +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/docs/troubleshooting.md +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/docs/user-guide.md +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/pyproject.toml +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/pytest.ini +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/requirements.txt +0 -0
- {aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aws-cis-controls-assessment
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.2
|
|
4
4
|
Summary: Production-ready AWS CIS Controls compliance assessment framework with 145 comprehensive rules
|
|
5
5
|
Author-email: AWS CIS Assessment Team <security@example.com>
|
|
6
6
|
Maintainer-email: AWS CIS Assessment Team <security@example.com>
|
|
@@ -6,6 +6,6 @@ CIS Controls Implementation Groups (IG1, IG2, IG3). Implements 163 comprehensive
|
|
|
6
6
|
across all implementation groups for complete security compliance assessment.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
-
__version__ = "1.1.
|
|
9
|
+
__version__ = "1.1.2"
|
|
10
10
|
__author__ = "AWS CIS Assessment Team"
|
|
11
11
|
__description__ = "Production-ready AWS CIS Controls Compliance Assessment Framework"
|
|
@@ -274,6 +274,8 @@ class HTMLReporter(ReportGenerator):
|
|
|
274
274
|
def _generate_html_body(self, html_data: Dict[str, Any]) -> str:
|
|
275
275
|
"""Generate HTML body section with content.
|
|
276
276
|
|
|
277
|
+
Modified in v1.1.2 to remove Detailed Findings and Remediation sections.
|
|
278
|
+
|
|
277
279
|
Args:
|
|
278
280
|
html_data: Enhanced HTML report data
|
|
279
281
|
|
|
@@ -285,9 +287,7 @@ class HTMLReporter(ReportGenerator):
|
|
|
285
287
|
navigation = self._generate_navigation(html_data)
|
|
286
288
|
executive_dashboard = self._generate_executive_dashboard(html_data)
|
|
287
289
|
implementation_groups = self._generate_implementation_groups_section(html_data)
|
|
288
|
-
detailed_findings = self._generate_detailed_findings_section(html_data)
|
|
289
290
|
resource_details = self._generate_resource_details_section(html_data)
|
|
290
|
-
remediation_section = self._generate_remediation_section(html_data)
|
|
291
291
|
footer = self._generate_footer(html_data)
|
|
292
292
|
|
|
293
293
|
body_content = f"""<body>
|
|
@@ -296,9 +296,7 @@ class HTMLReporter(ReportGenerator):
|
|
|
296
296
|
{navigation}
|
|
297
297
|
{executive_dashboard}
|
|
298
298
|
{implementation_groups}
|
|
299
|
-
{detailed_findings}
|
|
300
299
|
{resource_details}
|
|
301
|
-
{remediation_section}
|
|
302
300
|
{footer}
|
|
303
301
|
</div>
|
|
304
302
|
|
|
@@ -771,9 +769,9 @@ class HTMLReporter(ReportGenerator):
|
|
|
771
769
|
background-color: #2c3e50;
|
|
772
770
|
}
|
|
773
771
|
|
|
774
|
-
/* Resource ID column width constraint */
|
|
772
|
+
/* Resource ID column width constraint - increased to 220px in v1.1.2 */
|
|
775
773
|
.resource-table td:first-child {
|
|
776
|
-
max-width:
|
|
774
|
+
max-width: 220px;
|
|
777
775
|
overflow: hidden;
|
|
778
776
|
text-overflow: ellipsis;
|
|
779
777
|
white-space: nowrap;
|
|
@@ -785,6 +783,20 @@ class HTMLReporter(ReportGenerator):
|
|
|
785
783
|
word-wrap: break-word;
|
|
786
784
|
}
|
|
787
785
|
|
|
786
|
+
/* Resource Type column width constraint - added in v1.1.2 (reduced by 20%) */
|
|
787
|
+
.resource-table td:nth-child(2) {
|
|
788
|
+
max-width: 150px;
|
|
789
|
+
overflow: hidden;
|
|
790
|
+
text-overflow: ellipsis;
|
|
791
|
+
white-space: nowrap;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
.resource-table td:nth-child(2):hover {
|
|
795
|
+
overflow: visible;
|
|
796
|
+
white-space: normal;
|
|
797
|
+
word-wrap: break-word;
|
|
798
|
+
}
|
|
799
|
+
|
|
788
800
|
/* Visual frames around each resource row */
|
|
789
801
|
.resource-row {
|
|
790
802
|
border: 1px solid #e0e0e0;
|
|
@@ -1487,11 +1499,12 @@ class HTMLReporter(ReportGenerator):
|
|
|
1487
1499
|
window.URL.revokeObjectURL(url);
|
|
1488
1500
|
}}
|
|
1489
1501
|
|
|
1490
|
-
// Resource filtering functionality
|
|
1502
|
+
// Resource filtering functionality (updated in v1.1.2 to support Control filter)
|
|
1491
1503
|
function filterResources() {{
|
|
1492
1504
|
const searchTerm = document.getElementById('resourceSearch').value.toLowerCase();
|
|
1493
1505
|
const statusFilter = document.getElementById('statusFilter').value;
|
|
1494
1506
|
const typeFilter = document.getElementById('typeFilter').value;
|
|
1507
|
+
const controlFilter = document.getElementById('controlFilter').value;
|
|
1495
1508
|
const rows = document.querySelectorAll('#resourceTable tbody tr');
|
|
1496
1509
|
|
|
1497
1510
|
rows.forEach(function(row) {{
|
|
@@ -1500,6 +1513,7 @@ class HTMLReporter(ReportGenerator):
|
|
|
1500
1513
|
const resourceType = cells[1].textContent;
|
|
1501
1514
|
const status = cells[3].textContent.includes('COMPLIANT') ?
|
|
1502
1515
|
(cells[3].textContent.includes('NON_COMPLIANT') ? 'NON_COMPLIANT' : 'COMPLIANT') : 'NON_COMPLIANT';
|
|
1516
|
+
const controlId = cells[4].textContent;
|
|
1503
1517
|
const evaluationReason = cells[6].textContent.toLowerCase();
|
|
1504
1518
|
|
|
1505
1519
|
const matchesSearch = resourceId.includes(searchTerm) ||
|
|
@@ -1507,8 +1521,9 @@ class HTMLReporter(ReportGenerator):
|
|
|
1507
1521
|
evaluationReason.includes(searchTerm);
|
|
1508
1522
|
const matchesStatus = !statusFilter || status === statusFilter;
|
|
1509
1523
|
const matchesType = !typeFilter || resourceType === typeFilter;
|
|
1524
|
+
const matchesControl = !controlFilter || controlId === controlFilter;
|
|
1510
1525
|
|
|
1511
|
-
row.style.display = (matchesSearch && matchesStatus && matchesType) ? '' : 'none';
|
|
1526
|
+
row.style.display = (matchesSearch && matchesStatus && matchesType && matchesControl) ? '' : 'none';
|
|
1512
1527
|
}});
|
|
1513
1528
|
}}
|
|
1514
1529
|
|
|
@@ -1538,18 +1553,74 @@ class HTMLReporter(ReportGenerator):
|
|
|
1538
1553
|
}});
|
|
1539
1554
|
}}
|
|
1540
1555
|
|
|
1541
|
-
// Export resources to CSV
|
|
1556
|
+
// Export resources to CSV (updated in v1.1.2 with ARN, aggregate filtering, and port truncation)
|
|
1542
1557
|
function exportResourcesToCSV() {{
|
|
1543
1558
|
const table = document.getElementById('resourceTable');
|
|
1544
1559
|
const rows = table.querySelectorAll('tr');
|
|
1545
1560
|
let csvContent = '';
|
|
1546
1561
|
|
|
1547
|
-
|
|
1562
|
+
// Excluded aggregate row IDs (v1.1.2)
|
|
1563
|
+
const excludedIds = ['5631', '6460', '629'];
|
|
1564
|
+
|
|
1565
|
+
// Helper function to truncate port lists (v1.1.2)
|
|
1566
|
+
function truncatePortList(text) {{
|
|
1567
|
+
// Match port list patterns like [0, 1, 2, 3, ...]
|
|
1568
|
+
const portListRegex = /\\[(\\d+(?:,\\s*\\d+)*)\\]/g;
|
|
1569
|
+
|
|
1570
|
+
return text.replace(portListRegex, function(match, ports) {{
|
|
1571
|
+
const portArray = ports.split(',').map(p => p.trim());
|
|
1572
|
+
if (portArray.length > 10) {{
|
|
1573
|
+
const truncated = portArray.slice(0, 10).join(', ');
|
|
1574
|
+
return `[${{truncated}}, ...]`;
|
|
1575
|
+
}}
|
|
1576
|
+
return match;
|
|
1577
|
+
}});
|
|
1578
|
+
}}
|
|
1579
|
+
|
|
1580
|
+
rows.forEach(function(row, index) {{
|
|
1548
1581
|
const cells = row.querySelectorAll('th, td');
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1582
|
+
|
|
1583
|
+
// Handle header row - change "Resource ID" to "Resource ARN"
|
|
1584
|
+
if (index === 0) {{
|
|
1585
|
+
const headerData = Array.from(cells).map((cell, cellIndex) => {{
|
|
1586
|
+
let headerText = cell.textContent.replace(/\\s+/g, ' ').trim();
|
|
1587
|
+
// Replace "Resource ID" with "Resource ARN" in header
|
|
1588
|
+
if (cellIndex === 0 && headerText.includes('Resource ID')) {{
|
|
1589
|
+
headerText = headerText.replace('Resource ID', 'Resource ARN');
|
|
1590
|
+
}}
|
|
1591
|
+
return '"' + headerText.replace(/"/g, '""') + '"';
|
|
1592
|
+
}}).join(',');
|
|
1593
|
+
csvContent += headerData + '\\n';
|
|
1594
|
+
}} else if (cells.length > 0) {{
|
|
1595
|
+
// Get resource ID to check if it should be excluded
|
|
1596
|
+
const resourceIdCell = cells[0];
|
|
1597
|
+
const resourceId = resourceIdCell.textContent.replace(/\\s+/g, ' ').trim();
|
|
1598
|
+
|
|
1599
|
+
// Skip aggregate rows (v1.1.2)
|
|
1600
|
+
if (excludedIds.includes(resourceId)) {{
|
|
1601
|
+
return;
|
|
1602
|
+
}}
|
|
1603
|
+
|
|
1604
|
+
// Get ARN from data attribute or fall back to resource ID
|
|
1605
|
+
const resourceArn = resourceIdCell.getAttribute('data-arn') || resourceId;
|
|
1606
|
+
|
|
1607
|
+
const rowData = Array.from(cells).map((cell, cellIndex) => {{
|
|
1608
|
+
let cellText = cell.textContent.replace(/\\s+/g, ' ').trim();
|
|
1609
|
+
|
|
1610
|
+
// Use ARN for first column instead of Resource ID (v1.1.2)
|
|
1611
|
+
if (cellIndex === 0) {{
|
|
1612
|
+
cellText = resourceArn;
|
|
1613
|
+
}}
|
|
1614
|
+
|
|
1615
|
+
// Apply port list truncation to evaluation reason column (v1.1.2)
|
|
1616
|
+
if (cellIndex === 6) {{
|
|
1617
|
+
cellText = truncatePortList(cellText);
|
|
1618
|
+
}}
|
|
1619
|
+
|
|
1620
|
+
return '"' + cellText.replace(/"/g, '""') + '"';
|
|
1621
|
+
}}).join(',');
|
|
1622
|
+
csvContent += rowData + '\\n';
|
|
1623
|
+
}}
|
|
1553
1624
|
}});
|
|
1554
1625
|
|
|
1555
1626
|
const blob = new Blob([csvContent], {{ type: 'text/csv' }});
|
|
@@ -1849,161 +1920,7 @@ class HTMLReporter(ReportGenerator):
|
|
|
1849
1920
|
</section>
|
|
1850
1921
|
"""
|
|
1851
1922
|
|
|
1852
|
-
def _generate_detailed_findings_section(self, html_data: Dict[str, Any]) -> str:
|
|
1853
|
-
"""Generate detailed findings section.
|
|
1854
|
-
|
|
1855
|
-
This method consolidates findings by control ID only (not by IG) to eliminate
|
|
1856
|
-
duplication. Each control appears once with IG membership indicators.
|
|
1857
|
-
|
|
1858
|
-
Args:
|
|
1859
|
-
html_data: Enhanced HTML report data
|
|
1860
|
-
|
|
1861
|
-
Returns:
|
|
1862
|
-
Detailed findings HTML as string
|
|
1863
|
-
"""
|
|
1864
|
-
# Consolidate findings by control ID (deduplicated across IGs)
|
|
1865
|
-
consolidated_findings = self._consolidate_findings_by_control(
|
|
1866
|
-
html_data.get("implementation_groups", {})
|
|
1867
|
-
)
|
|
1868
|
-
|
|
1869
|
-
findings_content = ""
|
|
1870
|
-
|
|
1871
|
-
# Generate findings grouped by control ID only (sorted alphanumerically)
|
|
1872
|
-
for control_id, control_data in consolidated_findings.items():
|
|
1873
|
-
findings = control_data.get('findings', [])
|
|
1874
|
-
|
|
1875
|
-
# Skip if no non-compliant findings
|
|
1876
|
-
if not findings:
|
|
1877
|
-
continue
|
|
1878
|
-
|
|
1879
|
-
# Get control metadata
|
|
1880
|
-
config_rule_name = control_data.get('config_rule_name', '')
|
|
1881
|
-
title = control_data.get('title', f'CIS Control {control_id}')
|
|
1882
|
-
member_igs = control_data.get('member_igs', [])
|
|
1883
|
-
|
|
1884
|
-
# Format display name for collapsible header
|
|
1885
|
-
display_name = self._format_control_display_name(control_id, config_rule_name, title)
|
|
1886
|
-
|
|
1887
|
-
# Generate IG membership badges
|
|
1888
|
-
ig_badges_html = ""
|
|
1889
|
-
for ig in member_igs:
|
|
1890
|
-
badge_class = self._get_ig_badge_class(ig)
|
|
1891
|
-
ig_badges_html += f'<span class="ig-membership-badge {badge_class}">{ig}</span>'
|
|
1892
|
-
|
|
1893
|
-
# Generate findings table rows
|
|
1894
|
-
findings_rows = ""
|
|
1895
|
-
for finding in findings:
|
|
1896
|
-
if finding.get("compliance_status") == "NON_COMPLIANT":
|
|
1897
|
-
findings_rows += f"""
|
|
1898
|
-
<tr>
|
|
1899
|
-
<td>{finding.get('resource_id', 'N/A')}</td>
|
|
1900
|
-
<td>{finding.get('resource_type', 'N/A')}</td>
|
|
1901
|
-
<td>{finding.get('region', 'N/A')}</td>
|
|
1902
|
-
<td><span class="badge {finding.get('compliance_status', '').lower()}">{finding.get('compliance_status', 'UNKNOWN')}</span></td>
|
|
1903
|
-
<td>{finding.get('evaluation_reason', 'N/A')}</td>
|
|
1904
|
-
<td>{finding.get('config_rule_name', config_rule_name)}</td>
|
|
1905
|
-
</tr>
|
|
1906
|
-
"""
|
|
1907
|
-
|
|
1908
|
-
# Only add control section if there are non-compliant findings
|
|
1909
|
-
if findings_rows:
|
|
1910
|
-
non_compliant_count = len([f for f in findings if f.get('compliance_status') == 'NON_COMPLIANT'])
|
|
1911
|
-
|
|
1912
|
-
findings_content += f"""
|
|
1913
|
-
<div class="control-findings-section">
|
|
1914
|
-
<button class="collapsible">
|
|
1915
|
-
<span class="control-display-name">{display_name}</span>
|
|
1916
|
-
<span class="findings-count"> - Non-Compliant Resources ({non_compliant_count} items)</span>
|
|
1917
|
-
</button>
|
|
1918
|
-
<div class="collapsible-content">
|
|
1919
|
-
<div class="ig-membership-badges" style="margin-bottom: 15px;">
|
|
1920
|
-
<strong>Implementation Groups:</strong> {ig_badges_html}
|
|
1921
|
-
</div>
|
|
1922
|
-
<table class="findings-table">
|
|
1923
|
-
<thead>
|
|
1924
|
-
<tr>
|
|
1925
|
-
<th>Resource ID</th>
|
|
1926
|
-
<th>Resource Type</th>
|
|
1927
|
-
<th>Region</th>
|
|
1928
|
-
<th>Compliance Status</th>
|
|
1929
|
-
<th>Reason</th>
|
|
1930
|
-
<th>Config Rule</th>
|
|
1931
|
-
</tr>
|
|
1932
|
-
</thead>
|
|
1933
|
-
<tbody>
|
|
1934
|
-
{findings_rows}
|
|
1935
|
-
</tbody>
|
|
1936
|
-
</table>
|
|
1937
|
-
</div>
|
|
1938
|
-
</div>
|
|
1939
|
-
"""
|
|
1940
|
-
|
|
1941
|
-
return f"""
|
|
1942
|
-
<section id="detailed-findings" class="detailed-findings">
|
|
1943
|
-
<h2>Detailed Findings</h2>
|
|
1944
|
-
<p class="section-description">
|
|
1945
|
-
Findings are grouped by control ID and deduplicated across Implementation Groups.
|
|
1946
|
-
Each control shows which IGs include it.
|
|
1947
|
-
</p>
|
|
1948
|
-
<div class="search-container">
|
|
1949
|
-
<input type="text" placeholder="Search findings..." onkeyup="searchFindings(this.value)" style="width: 100%; padding: 10px; margin-bottom: 20px; border: 1px solid #ddd; border-radius: 5px;">
|
|
1950
|
-
</div>
|
|
1951
|
-
{findings_content if findings_content else '<p>No non-compliant findings to display.</p>'}
|
|
1952
|
-
</section>
|
|
1953
|
-
"""
|
|
1954
1923
|
|
|
1955
|
-
def _generate_remediation_section(self, html_data: Dict[str, Any]) -> str:
|
|
1956
|
-
"""Generate remediation section.
|
|
1957
|
-
|
|
1958
|
-
Args:
|
|
1959
|
-
html_data: Enhanced HTML report data
|
|
1960
|
-
|
|
1961
|
-
Returns:
|
|
1962
|
-
Remediation HTML as string
|
|
1963
|
-
"""
|
|
1964
|
-
remediation_items = ""
|
|
1965
|
-
|
|
1966
|
-
for remediation in html_data["remediation_priorities"]:
|
|
1967
|
-
steps_html = ""
|
|
1968
|
-
for step in remediation["remediation_steps"]:
|
|
1969
|
-
steps_html += f"<li>{step}</li>"
|
|
1970
|
-
|
|
1971
|
-
# Normalize priority and effort text to proper capitalization
|
|
1972
|
-
priority_text = remediation['priority'].capitalize()
|
|
1973
|
-
effort_text = remediation['estimated_effort'].capitalize()
|
|
1974
|
-
|
|
1975
|
-
remediation_items += f"""
|
|
1976
|
-
<div class="remediation-item">
|
|
1977
|
-
<div class="remediation-header">
|
|
1978
|
-
<div>
|
|
1979
|
-
<h4>{remediation['control_id']} - {remediation['config_rule_name']}</h4>
|
|
1980
|
-
</div>
|
|
1981
|
-
<div class="remediation-badges">
|
|
1982
|
-
<span class="badge {remediation['priority_badge']}">{priority_text}</span>
|
|
1983
|
-
<span class="badge {remediation['effort_badge']}">{effort_text}</span>
|
|
1984
|
-
</div>
|
|
1985
|
-
</div>
|
|
1986
|
-
<div class="remediation-content">
|
|
1987
|
-
<h5>Remediation Steps:</h5>
|
|
1988
|
-
<ol>
|
|
1989
|
-
{steps_html}
|
|
1990
|
-
</ol>
|
|
1991
|
-
<p><strong>Documentation:</strong> <a href="{remediation['aws_documentation_link']}" target="_blank">AWS Documentation</a></p>
|
|
1992
|
-
</div>
|
|
1993
|
-
</div>
|
|
1994
|
-
"""
|
|
1995
|
-
|
|
1996
|
-
return f"""
|
|
1997
|
-
<section id="remediation" class="remediation">
|
|
1998
|
-
<h2>Remediation Priorities</h2>
|
|
1999
|
-
<div class="remediation-list">
|
|
2000
|
-
{remediation_items}
|
|
2001
|
-
</div>
|
|
2002
|
-
<div class="export-actions">
|
|
2003
|
-
<button onclick="exportToCSV()" class="export-btn">Export Findings to CSV</button>
|
|
2004
|
-
</div>
|
|
2005
|
-
</section>
|
|
2006
|
-
"""
|
|
2007
1924
|
|
|
2008
1925
|
def _generate_footer(self, html_data: Dict[str, Any]) -> str:
|
|
2009
1926
|
"""Generate footer section.
|
|
@@ -2109,6 +2026,8 @@ class HTMLReporter(ReportGenerator):
|
|
|
2109
2026
|
def _build_navigation_structure(self, html_data: Dict[str, Any]) -> Dict[str, Any]:
|
|
2110
2027
|
"""Build navigation structure for the report.
|
|
2111
2028
|
|
|
2029
|
+
Modified in v1.1.2 to remove Detailed Findings and Remediation sections.
|
|
2030
|
+
|
|
2112
2031
|
Args:
|
|
2113
2032
|
html_data: Enhanced HTML report data
|
|
2114
2033
|
|
|
@@ -2119,9 +2038,7 @@ class HTMLReporter(ReportGenerator):
|
|
|
2119
2038
|
"sections": [
|
|
2120
2039
|
{"id": "dashboard", "title": "Dashboard"},
|
|
2121
2040
|
{"id": "implementation-groups", "title": "Implementation Groups"},
|
|
2122
|
-
{"id": "
|
|
2123
|
-
{"id": "resource-details", "title": "Resource Details"},
|
|
2124
|
-
{"id": "remediation", "title": "Remediation"}
|
|
2041
|
+
{"id": "resource-details", "title": "Resource Details"}
|
|
2125
2042
|
]
|
|
2126
2043
|
}
|
|
2127
2044
|
|
|
@@ -2594,9 +2511,14 @@ class HTMLReporter(ReportGenerator):
|
|
|
2594
2511
|
status_class = "compliant" if resource["compliance_status"] == "COMPLIANT" else "non_compliant"
|
|
2595
2512
|
status_icon = "✓" if resource["compliance_status"] == "COMPLIANT" else "✗"
|
|
2596
2513
|
|
|
2514
|
+
# Construct pseudo-ARN for CSV export (v1.1.2)
|
|
2515
|
+
# Format: arn:aws:service:region:account:resource-type/resource-id
|
|
2516
|
+
# Since we don't have account ID in resource data, we'll use a placeholder
|
|
2517
|
+
resource_arn = f"arn:aws:{resource['resource_type'].split('::')[1].lower() if '::' in resource['resource_type'] else 'unknown'}:{resource['region']}:*:{resource['resource_id']}"
|
|
2518
|
+
|
|
2597
2519
|
resource_rows += f"""
|
|
2598
2520
|
<tr class="resource-row {status_class}">
|
|
2599
|
-
<td><code>{resource['resource_id']}</code></td>
|
|
2521
|
+
<td data-arn="{resource_arn}"><code>{resource['resource_id']}</code></td>
|
|
2600
2522
|
<td>{resource['resource_type']}</td>
|
|
2601
2523
|
<td>{resource['region']}</td>
|
|
2602
2524
|
<td>
|
|
@@ -2616,6 +2538,12 @@ class HTMLReporter(ReportGenerator):
|
|
|
2616
2538
|
non_compliant_resources = total_resources - compliant_resources
|
|
2617
2539
|
compliance_percentage = (compliant_resources / total_resources * 100) if total_resources > 0 else 0
|
|
2618
2540
|
|
|
2541
|
+
# Extract unique Control IDs for filter dropdown (v1.1.2)
|
|
2542
|
+
unique_control_ids = sorted(set(r["control_id"] for r in all_resources), key=self._sort_control_id)
|
|
2543
|
+
control_filter_options = ""
|
|
2544
|
+
for control_id in unique_control_ids:
|
|
2545
|
+
control_filter_options += f'<option value="{control_id}">{control_id}</option>'
|
|
2546
|
+
|
|
2619
2547
|
# Generate resource type breakdown
|
|
2620
2548
|
resource_type_stats = {}
|
|
2621
2549
|
for resource in all_resources:
|
|
@@ -2689,6 +2617,10 @@ class HTMLReporter(ReportGenerator):
|
|
|
2689
2617
|
<option value="">All Types</option>
|
|
2690
2618
|
{self._generate_resource_type_options(resource_type_stats)}
|
|
2691
2619
|
</select>
|
|
2620
|
+
<select id="controlFilter" onchange="filterResources()" class="filter-select">
|
|
2621
|
+
<option value="">All Controls</option>
|
|
2622
|
+
{control_filter_options}
|
|
2623
|
+
</select>
|
|
2692
2624
|
</div>
|
|
2693
2625
|
|
|
2694
2626
|
<table class="findings-table resource-table" id="resourceTable">
|
|
@@ -2859,6 +2791,8 @@ class HTMLReporter(ReportGenerator):
|
|
|
2859
2791
|
Enriches control data with additional fields needed for improved display,
|
|
2860
2792
|
including formatted names, IG membership badges, and truncation indicators.
|
|
2861
2793
|
|
|
2794
|
+
Modified in v1.1.2 to remove duplicate Control ID prefix from display names.
|
|
2795
|
+
|
|
2862
2796
|
Args:
|
|
2863
2797
|
control_data: Existing control data dictionary
|
|
2864
2798
|
control_id: Control identifier (e.g., "1.5")
|
|
@@ -2867,7 +2801,7 @@ class HTMLReporter(ReportGenerator):
|
|
|
2867
2801
|
|
|
2868
2802
|
Returns:
|
|
2869
2803
|
Enhanced control data with additional fields:
|
|
2870
|
-
- display_name: Formatted name
|
|
2804
|
+
- display_name: Formatted name without duplicate Control ID prefix
|
|
2871
2805
|
- originating_ig: Which IG introduced this control (IG1, IG2, or IG3)
|
|
2872
2806
|
- ig_badge_class: CSS class for IG badge styling
|
|
2873
2807
|
- needs_truncation: Boolean indicating if display name exceeds 50 characters
|
|
@@ -2884,7 +2818,7 @@ class HTMLReporter(ReportGenerator):
|
|
|
2884
2818
|
Output enriched data (includes all input fields plus):
|
|
2885
2819
|
{
|
|
2886
2820
|
...original fields...,
|
|
2887
|
-
'display_name': '
|
|
2821
|
+
'display_name': 'root-account-hardware-mfa-enabled', # No "1.5:" prefix
|
|
2888
2822
|
'originating_ig': 'IG1',
|
|
2889
2823
|
'ig_badge_class': 'ig-badge-1',
|
|
2890
2824
|
'needs_truncation': False
|
|
@@ -2895,16 +2829,25 @@ class HTMLReporter(ReportGenerator):
|
|
|
2895
2829
|
- Truncation threshold is 50 characters
|
|
2896
2830
|
- Gracefully handles missing config_rule_name
|
|
2897
2831
|
- Originating IG is determined by checking IG1, IG2, IG3 in order
|
|
2832
|
+
- v1.1.2: Removes duplicate Control ID prefix from display names
|
|
2898
2833
|
"""
|
|
2899
2834
|
enriched = control_data.copy()
|
|
2900
2835
|
|
|
2901
2836
|
# Format display name
|
|
2902
|
-
|
|
2837
|
+
display_name = self._format_control_display_name(
|
|
2903
2838
|
control_id,
|
|
2904
2839
|
control_data.get('config_rule_name', ''),
|
|
2905
2840
|
control_data.get('title')
|
|
2906
2841
|
)
|
|
2907
2842
|
|
|
2843
|
+
# Remove duplicate Control ID prefix if present (v1.1.2 improvement)
|
|
2844
|
+
# Check if display_name starts with "control_id: "
|
|
2845
|
+
prefix = f"{control_id}: "
|
|
2846
|
+
if display_name.startswith(prefix):
|
|
2847
|
+
display_name = display_name[len(prefix):]
|
|
2848
|
+
|
|
2849
|
+
enriched['display_name'] = display_name
|
|
2850
|
+
|
|
2908
2851
|
# Determine originating IG (which IG introduced this control)
|
|
2909
2852
|
originating_ig = self._determine_originating_ig(control_id, all_igs)
|
|
2910
2853
|
enriched['originating_ig'] = originating_ig
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aws-cis-controls-assessment
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.2
|
|
4
4
|
Summary: Production-ready AWS CIS Controls compliance assessment framework with 145 comprehensive rules
|
|
5
5
|
Author-email: AWS CIS Assessment Team <security@example.com>
|
|
6
6
|
Maintainer-email: AWS CIS Assessment Team <security@example.com>
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/docs/assessment-logic.md
RENAMED
|
File without changes
|
{aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/docs/cli-reference.md
RENAMED
|
File without changes
|
{aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/docs/config-rule-mappings.md
RENAMED
|
File without changes
|
{aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/docs/developer-guide.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/docs/installation.md
RENAMED
|
File without changes
|
|
File without changes
|
{aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/docs/scoring-methodology.md
RENAMED
|
File without changes
|
{aws_cis_controls_assessment-1.1.1 → aws_cis_controls_assessment-1.1.2}/docs/troubleshooting.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|