aws-cis-controls-assessment 1.1.2__tar.gz → 1.1.4__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.
Files changed (95) hide show
  1. {aws_cis_controls_assessment-1.1.2/aws_cis_controls_assessment.egg-info → aws_cis_controls_assessment-1.1.4}/PKG-INFO +1 -1
  2. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/__init__.py +1 -1
  3. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/ig1/control_cloudtrail_logging.py +42 -6
  4. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/reporters/html_reporter.py +20 -21
  5. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4/aws_cis_controls_assessment.egg-info}/PKG-INFO +1 -1
  6. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/LICENSE +0 -0
  7. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/MANIFEST.in +0 -0
  8. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/README.md +0 -0
  9. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/cli/__init__.py +0 -0
  10. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/cli/examples.py +0 -0
  11. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/cli/main.py +0 -0
  12. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/cli/utils.py +0 -0
  13. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/config/__init__.py +0 -0
  14. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/config/config_loader.py +0 -0
  15. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/config/rules/cis_controls_ig1.yaml +0 -0
  16. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/config/rules/cis_controls_ig2.yaml +0 -0
  17. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/config/rules/cis_controls_ig3.yaml +0 -0
  18. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/__init__.py +0 -0
  19. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/base_control.py +0 -0
  20. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/ig1/__init__.py +0 -0
  21. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/ig1/control_1_1.py +0 -0
  22. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/ig1/control_2_2.py +0 -0
  23. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/ig1/control_3_3.py +0 -0
  24. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/ig1/control_3_4.py +0 -0
  25. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/ig1/control_4_1.py +0 -0
  26. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/ig1/control_access_keys.py +0 -0
  27. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/ig1/control_advanced_security.py +0 -0
  28. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/ig1/control_aws_backup_service.py +0 -0
  29. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/ig1/control_backup_recovery.py +0 -0
  30. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/ig1/control_critical_security.py +0 -0
  31. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/ig1/control_data_protection.py +0 -0
  32. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/ig1/control_iam_advanced.py +0 -0
  33. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/ig1/control_iam_governance.py +0 -0
  34. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/ig1/control_iam_policies.py +0 -0
  35. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/ig1/control_instance_optimization.py +0 -0
  36. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/ig1/control_network_enhancements.py +0 -0
  37. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/ig1/control_network_security.py +0 -0
  38. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/ig1/control_s3_enhancements.py +0 -0
  39. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/ig1/control_s3_security.py +0 -0
  40. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/ig1/control_vpc_security.py +0 -0
  41. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/ig2/__init__.py +0 -0
  42. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/ig2/control_3_10.py +0 -0
  43. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/ig2/control_3_11.py +0 -0
  44. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/ig2/control_4_5_6_access_configuration.py +0 -0
  45. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/ig2/control_5_2.py +0 -0
  46. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/ig2/control_8_audit_logging.py +0 -0
  47. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/ig2/control_advanced_encryption.py +0 -0
  48. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/ig2/control_aws_backup_ig2.py +0 -0
  49. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/ig2/control_codebuild_security.py +0 -0
  50. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/ig2/control_encryption_rest.py +0 -0
  51. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/ig2/control_encryption_transit.py +0 -0
  52. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/ig2/control_network_ha.py +0 -0
  53. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/ig2/control_remaining_encryption.py +0 -0
  54. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/ig2/control_remaining_rules.py +0 -0
  55. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/ig2/control_service_logging.py +0 -0
  56. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/ig3/__init__.py +0 -0
  57. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/ig3/control_12_8.py +0 -0
  58. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/ig3/control_13_1.py +0 -0
  59. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/ig3/control_3_14.py +0 -0
  60. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/controls/ig3/control_7_1.py +0 -0
  61. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/core/__init__.py +0 -0
  62. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/core/accuracy_validator.py +0 -0
  63. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/core/assessment_engine.py +0 -0
  64. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/core/audit_trail.py +0 -0
  65. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/core/aws_client_factory.py +0 -0
  66. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/core/error_handler.py +0 -0
  67. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/core/models.py +0 -0
  68. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/core/scoring_engine.py +0 -0
  69. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/reporters/__init__.py +0 -0
  70. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/reporters/base_reporter.py +0 -0
  71. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/reporters/csv_reporter.py +0 -0
  72. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_assessment/reporters/json_reporter.py +0 -0
  73. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_controls_assessment.egg-info/SOURCES.txt +0 -0
  74. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_controls_assessment.egg-info/dependency_links.txt +0 -0
  75. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_controls_assessment.egg-info/entry_points.txt +0 -0
  76. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_controls_assessment.egg-info/requires.txt +0 -0
  77. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/aws_cis_controls_assessment.egg-info/top_level.txt +0 -0
  78. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/deprecation-package/aws_cis_assessment_deprecated/__init__.py +0 -0
  79. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/docs/README.md +0 -0
  80. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/docs/adding-aws-backup-controls.md +0 -0
  81. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/docs/assessment-logic.md +0 -0
  82. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/docs/cli-reference.md +0 -0
  83. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/docs/config-rule-mappings.md +0 -0
  84. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/docs/developer-guide.md +0 -0
  85. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/docs/dual-scoring-implementation.md +0 -0
  86. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/docs/html-report-improvements.md +0 -0
  87. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/docs/installation.md +0 -0
  88. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/docs/scoring-comparison-aws-config.md +0 -0
  89. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/docs/scoring-methodology.md +0 -0
  90. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/docs/troubleshooting.md +0 -0
  91. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/docs/user-guide.md +0 -0
  92. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/pyproject.toml +0 -0
  93. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/pytest.ini +0 -0
  94. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/requirements.txt +0 -0
  95. {aws_cis_controls_assessment-1.1.2 → aws_cis_controls_assessment-1.1.4}/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.2
3
+ Version: 1.1.4
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.2"
9
+ __version__ = "1.1.4"
10
10
  __author__ = "AWS CIS Assessment Team"
11
11
  __description__ = "Production-ready AWS CIS Controls Compliance Assessment Framework"
@@ -39,8 +39,9 @@ class CloudTrailEnabledAssessment(BaseConfigRuleAssessment):
39
39
  try:
40
40
  cloudtrail_client = aws_factory.get_client('cloudtrail', region)
41
41
 
42
- # Get all trails in this region
43
- response = cloudtrail_client.describe_trails()
42
+ # Get all trails in this region, excluding shadow trails
43
+ # Shadow trails are replications from other regions or organization trails
44
+ response = cloudtrail_client.describe_trails(includeShadowTrails=False)
44
45
  trails = response.get('trailList', [])
45
46
 
46
47
  # Get trail status for each trail
@@ -48,6 +49,13 @@ class CloudTrailEnabledAssessment(BaseConfigRuleAssessment):
48
49
  for trail in trails:
49
50
  trail_name = trail.get('Name', '')
50
51
  trail_arn = trail.get('TrailARN', '')
52
+ home_region = trail.get('HomeRegion', '')
53
+
54
+ # Skip shadow trails (trails from other regions or organization trails)
55
+ # These are indicated by HomeRegion being different from current region
56
+ if home_region and home_region != region:
57
+ logger.debug(f"Skipping shadow trail {trail_name} (home region: {home_region}, current region: {region})")
58
+ continue
51
59
 
52
60
  try:
53
61
  # Get trail status
@@ -66,14 +74,22 @@ class CloudTrailEnabledAssessment(BaseConfigRuleAssessment):
66
74
  'TrailARN': trail_arn,
67
75
  'IsLogging': is_logging,
68
76
  'IsMultiRegionTrail': trail.get('IsMultiRegionTrail', False),
77
+ 'IsOrganizationTrail': trail.get('IsOrganizationTrail', False),
69
78
  'IncludeGlobalServiceEvents': trail.get('IncludeGlobalServiceEvents', False),
70
79
  'S3BucketName': trail.get('S3BucketName', ''),
80
+ 'HomeRegion': home_region,
71
81
  'EventSelectors': event_selectors,
72
82
  'Region': region
73
83
  })
74
84
 
75
85
  except ClientError as e:
76
- logger.warning(f"Error getting status for trail {trail_name}: {e}")
86
+ error_code = e.response.get('Error', {}).get('Code', '')
87
+
88
+ # Only log warning for unexpected errors, not for shadow trails
89
+ if error_code == 'TrailNotFoundException':
90
+ logger.debug(f"Trail {trail_name} not found in {region} - likely a shadow trail or deleted trail")
91
+ else:
92
+ logger.warning(f"Error getting status for trail {trail_name}: {e}")
77
93
  continue
78
94
 
79
95
  # Return account-level resource with trail information
@@ -100,13 +116,33 @@ class CloudTrailEnabledAssessment(BaseConfigRuleAssessment):
100
116
  if has_active_trails:
101
117
  # Check for at least one properly configured trail
102
118
  active_trails = [trail for trail in trails if trail['IsLogging']]
103
- trail_names = [trail['TrailName'] for trail in active_trails]
119
+
120
+ # Categorize trails
121
+ org_trails = [t for t in active_trails if t.get('IsOrganizationTrail', False)]
122
+ multi_region_trails = [t for t in active_trails if t.get('IsMultiRegionTrail', False)]
123
+ regional_trails = [t for t in active_trails if not t.get('IsMultiRegionTrail', False)]
124
+
125
+ # Build detailed reason
126
+ trail_details = []
127
+ for trail in active_trails:
128
+ trail_type = []
129
+ if trail.get('IsOrganizationTrail', False):
130
+ trail_type.append("organization")
131
+ if trail.get('IsMultiRegionTrail', False):
132
+ trail_type.append("multi-region")
133
+ else:
134
+ trail_type.append("regional")
135
+
136
+ trail_info = f"{trail['TrailName']} ({', '.join(trail_type)})"
137
+ trail_details.append(trail_info)
138
+
139
+ reason = f"CloudTrail is enabled with {len(active_trails)} active trail(s): {', '.join(trail_details)}"
104
140
 
105
141
  return ComplianceResult(
106
142
  resource_id=account_id,
107
143
  resource_type="AWS::::Account",
108
144
  compliance_status=ComplianceStatus.COMPLIANT,
109
- evaluation_reason=f"CloudTrail is enabled with {len(active_trails)} active trail(s): {', '.join(trail_names)}",
145
+ evaluation_reason=reason,
110
146
  config_rule_name=self.rule_name,
111
147
  region=region
112
148
  )
@@ -115,7 +151,7 @@ class CloudTrailEnabledAssessment(BaseConfigRuleAssessment):
115
151
  resource_id=account_id,
116
152
  resource_type="AWS::::Account",
117
153
  compliance_status=ComplianceStatus.NON_COMPLIANT,
118
- evaluation_reason="CloudTrail is not enabled or no trails are actively logging",
154
+ evaluation_reason="CloudTrail is not enabled or no trails are actively logging in this region",
119
155
  config_rule_name=self.rule_name,
120
156
  region=region
121
157
  )
@@ -1623,7 +1623,8 @@ class HTMLReporter(ReportGenerator):
1623
1623
  }}
1624
1624
  }});
1625
1625
 
1626
- const blob = new Blob([csvContent], {{ type: 'text/csv' }});
1626
+ // Add UTF-8 BOM to ensure proper encoding in Excel and other tools
1627
+ const blob = new Blob(['\ufeff' + csvContent], {{ type: 'text/csv;charset=utf-8;' }});
1627
1628
  const url = window.URL.createObjectURL(blob);
1628
1629
  const a = document.createElement('a');
1629
1630
  a.href = url;
@@ -1751,14 +1752,14 @@ class HTMLReporter(ReportGenerator):
1751
1752
 
1752
1753
  <div class="metric-card">
1753
1754
  <div class="metric-value">{exec_summary.get('total_resources', 0):,}</div>
1754
- <div class="metric-label">Resources Evaluated</div>
1755
- <div class="metric-trend trend-up">Across {len(metadata.get('regions_assessed', []))} regions</div>
1755
+ <div class="metric-label">Resource Evaluations</div>
1756
+ <div class="metric-trend trend-up">Across {len(metadata.get('regions_assessed', []))} regions and multiple controls</div>
1756
1757
  </div>
1757
1758
 
1758
1759
  <div class="metric-card">
1759
1760
  <div class="metric-value">{exec_summary.get('compliant_resources', 0):,}</div>
1760
- <div class="metric-label">Compliant Resources</div>
1761
- <div class="metric-trend trend-up">{(exec_summary.get('compliant_resources', 0) / max(exec_summary.get('total_resources', 1), 1) * 100):.1f}% of total</div>
1761
+ <div class="metric-label">Compliant Evaluations</div>
1762
+ <div class="metric-trend trend-up">{(exec_summary.get('compliant_resources', 0) / max(exec_summary.get('total_resources', 1), 1) * 100):.1f}% of evaluations</div>
1762
1763
  </div>
1763
1764
  """
1764
1765
 
@@ -1946,7 +1947,8 @@ class HTMLReporter(ReportGenerator):
1946
1947
  <h4>Assessment Scope</h4>
1947
1948
  <p>AWS Account: {metadata.get('account_id', 'Unknown')}</p>
1948
1949
  <p>Regions: {', '.join(metadata.get('regions_assessed', []))}</p>
1949
- <p>Total Resources: {metadata.get('total_resources_evaluated', 0):,}</p>
1950
+ <p>Resource Evaluations: {metadata.get('total_resources_evaluated', 0):,}</p>
1951
+ <p style="font-size: 0.85em; color: #999; margin-top: 5px;">Note: Same resource may be evaluated by multiple controls</p>
1950
1952
  </div>
1951
1953
  <div class="footer-section">
1952
1954
  <h4>About CIS Controls</h4>
@@ -1955,7 +1957,7 @@ class HTMLReporter(ReportGenerator):
1955
1957
  </div>
1956
1958
  </div>
1957
1959
  <div class="footer-bottom">
1958
- <p>&copy; 2024 AWS CIS Assessment Tool. Generated with HTML Reporter v{html_data.get('report_version', '1.0')}</p>
1960
+ <p>&copy; {datetime.now().year} AWS CIS Assessment Tool. Generated with HTML Reporter v{html_data.get('report_version', '1.1.2')}</p>
1959
1961
  </div>
1960
1962
  </footer>
1961
1963
  """
@@ -2509,7 +2511,7 @@ class HTMLReporter(ReportGenerator):
2509
2511
  resource_rows = ""
2510
2512
  for resource in all_resources:
2511
2513
  status_class = "compliant" if resource["compliance_status"] == "COMPLIANT" else "non_compliant"
2512
- status_icon = "" if resource["compliance_status"] == "COMPLIANT" else ""
2514
+ status_text = "COMPLIANT" if resource["compliance_status"] == "COMPLIANT" else "NON_COMPLIANT"
2513
2515
 
2514
2516
  # Construct pseudo-ARN for CSV export (v1.1.2)
2515
2517
  # Format: arn:aws:service:region:account:resource-type/resource-id
@@ -2523,7 +2525,7 @@ class HTMLReporter(ReportGenerator):
2523
2525
  <td>{resource['region']}</td>
2524
2526
  <td>
2525
2527
  <span class="badge {status_class}">
2526
- {status_icon} {resource['compliance_status']}
2528
+ {status_text}
2527
2529
  </span>
2528
2530
  </td>
2529
2531
  <td>{resource['control_id']}</td>
@@ -2558,6 +2560,8 @@ class HTMLReporter(ReportGenerator):
2558
2560
  for resource_type, stats in sorted(resource_type_stats.items()):
2559
2561
  type_compliance = (stats["compliant"] / stats["total"] * 100) if stats["total"] > 0 else 0
2560
2562
  status_class = self._get_status_class(type_compliance)
2563
+ # Ensure minimum width of 5% for visibility when compliance is 0%
2564
+ display_width = max(type_compliance, 5.0) if type_compliance < 5.0 else type_compliance
2561
2565
 
2562
2566
  resource_type_breakdown += f"""
2563
2567
  <div class="resource-type-stat">
@@ -2566,7 +2570,7 @@ class HTMLReporter(ReportGenerator):
2566
2570
  <span class="resource-type-count">{stats['compliant']}/{stats['total']}</span>
2567
2571
  </div>
2568
2572
  <div class="progress-container">
2569
- <div class="progress-bar {status_class}" data-width="{type_compliance}">
2573
+ <div class="progress-bar {status_class}" data-width="{display_width}">
2570
2574
  <span class="progress-text">{type_compliance:.1f}%</span>
2571
2575
  </div>
2572
2576
  </div>
@@ -2626,12 +2630,12 @@ class HTMLReporter(ReportGenerator):
2626
2630
  <table class="findings-table resource-table" id="resourceTable">
2627
2631
  <thead>
2628
2632
  <tr>
2629
- <th onclick="sortResourceTable(0)">Resource ID ↕</th>
2630
- <th onclick="sortResourceTable(1)">Resource Type ↕</th>
2631
- <th onclick="sortResourceTable(2)">Region ↕</th>
2632
- <th onclick="sortResourceTable(3)">Status ↕</th>
2633
- <th onclick="sortResourceTable(4)">Control ↕</th>
2634
- <th onclick="sortResourceTable(5)">Config Rule ↕</th>
2633
+ <th onclick="sortResourceTable(0)">Resource ID</th>
2634
+ <th onclick="sortResourceTable(1)">Resource Type</th>
2635
+ <th onclick="sortResourceTable(2)">Region</th>
2636
+ <th onclick="sortResourceTable(3)">Status</th>
2637
+ <th onclick="sortResourceTable(4)">Control</th>
2638
+ <th onclick="sortResourceTable(5)">Config Rule</th>
2635
2639
  <th>Evaluation Details</th>
2636
2640
  </tr>
2637
2641
  </thead>
@@ -2640,11 +2644,6 @@ class HTMLReporter(ReportGenerator):
2640
2644
  </tbody>
2641
2645
  </table>
2642
2646
  </div>
2643
-
2644
- <div class="resource-export">
2645
- <button onclick="exportResourcesToCSV()" class="export-btn">Export to CSV</button>
2646
- <button onclick="exportResourcesToJSON()" class="export-btn">Export to JSON</button>
2647
- </div>
2648
2647
  </section>
2649
2648
  """
2650
2649
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aws-cis-controls-assessment
3
- Version: 1.1.2
3
+ Version: 1.1.4
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>