runbooks 0.7.6__py3-none-any.whl → 0.7.9__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.
Files changed (111) hide show
  1. runbooks/__init__.py +1 -1
  2. runbooks/base.py +5 -1
  3. runbooks/cfat/__init__.py +8 -4
  4. runbooks/cfat/assessment/collectors.py +171 -14
  5. runbooks/cfat/assessment/compliance.py +871 -0
  6. runbooks/cfat/assessment/runner.py +122 -11
  7. runbooks/cfat/models.py +6 -2
  8. runbooks/common/logger.py +14 -0
  9. runbooks/common/rich_utils.py +451 -0
  10. runbooks/enterprise/__init__.py +68 -0
  11. runbooks/enterprise/error_handling.py +411 -0
  12. runbooks/enterprise/logging.py +439 -0
  13. runbooks/enterprise/multi_tenant.py +583 -0
  14. runbooks/finops/README.md +468 -241
  15. runbooks/finops/__init__.py +39 -3
  16. runbooks/finops/cli.py +83 -18
  17. runbooks/finops/cross_validation.py +375 -0
  18. runbooks/finops/dashboard_runner.py +812 -164
  19. runbooks/finops/enhanced_dashboard_runner.py +525 -0
  20. runbooks/finops/finops_dashboard.py +1892 -0
  21. runbooks/finops/helpers.py +485 -51
  22. runbooks/finops/optimizer.py +823 -0
  23. runbooks/finops/tests/__init__.py +19 -0
  24. runbooks/finops/tests/results_test_finops_dashboard.xml +1 -0
  25. runbooks/finops/tests/run_comprehensive_tests.py +421 -0
  26. runbooks/finops/tests/run_tests.py +305 -0
  27. runbooks/finops/tests/test_finops_dashboard.py +705 -0
  28. runbooks/finops/tests/test_integration.py +477 -0
  29. runbooks/finops/tests/test_performance.py +380 -0
  30. runbooks/finops/tests/test_performance_benchmarks.py +500 -0
  31. runbooks/finops/tests/test_reference_images_validation.py +867 -0
  32. runbooks/finops/tests/test_single_account_features.py +715 -0
  33. runbooks/finops/tests/validate_test_suite.py +220 -0
  34. runbooks/finops/types.py +1 -1
  35. runbooks/hitl/enhanced_workflow_engine.py +725 -0
  36. runbooks/inventory/artifacts/scale-optimize-status.txt +12 -0
  37. runbooks/inventory/collectors/aws_comprehensive.py +442 -0
  38. runbooks/inventory/collectors/enterprise_scale.py +281 -0
  39. runbooks/inventory/core/collector.py +172 -13
  40. runbooks/inventory/discovery.md +1 -1
  41. runbooks/inventory/list_ec2_instances.py +18 -20
  42. runbooks/inventory/list_ssm_parameters.py +31 -3
  43. runbooks/inventory/organizations_discovery.py +1269 -0
  44. runbooks/inventory/rich_inventory_display.py +393 -0
  45. runbooks/inventory/run_on_multi_accounts.py +35 -19
  46. runbooks/inventory/runbooks.security.report_generator.log +0 -0
  47. runbooks/inventory/runbooks.security.run_script.log +0 -0
  48. runbooks/inventory/vpc_flow_analyzer.py +1030 -0
  49. runbooks/main.py +2215 -119
  50. runbooks/metrics/dora_metrics_engine.py +599 -0
  51. runbooks/operate/__init__.py +2 -2
  52. runbooks/operate/base.py +122 -10
  53. runbooks/operate/deployment_framework.py +1032 -0
  54. runbooks/operate/deployment_validator.py +853 -0
  55. runbooks/operate/dynamodb_operations.py +10 -6
  56. runbooks/operate/ec2_operations.py +319 -11
  57. runbooks/operate/executive_dashboard.py +779 -0
  58. runbooks/operate/mcp_integration.py +750 -0
  59. runbooks/operate/nat_gateway_operations.py +1120 -0
  60. runbooks/operate/networking_cost_heatmap.py +685 -0
  61. runbooks/operate/privatelink_operations.py +940 -0
  62. runbooks/operate/s3_operations.py +10 -6
  63. runbooks/operate/vpc_endpoints.py +644 -0
  64. runbooks/operate/vpc_operations.py +1038 -0
  65. runbooks/remediation/__init__.py +2 -2
  66. runbooks/remediation/acm_remediation.py +1 -1
  67. runbooks/remediation/base.py +1 -1
  68. runbooks/remediation/cloudtrail_remediation.py +1 -1
  69. runbooks/remediation/cognito_remediation.py +1 -1
  70. runbooks/remediation/dynamodb_remediation.py +1 -1
  71. runbooks/remediation/ec2_remediation.py +1 -1
  72. runbooks/remediation/ec2_unattached_ebs_volumes.py +1 -1
  73. runbooks/remediation/kms_enable_key_rotation.py +1 -1
  74. runbooks/remediation/kms_remediation.py +1 -1
  75. runbooks/remediation/lambda_remediation.py +1 -1
  76. runbooks/remediation/multi_account.py +1 -1
  77. runbooks/remediation/rds_remediation.py +1 -1
  78. runbooks/remediation/s3_block_public_access.py +1 -1
  79. runbooks/remediation/s3_enable_access_logging.py +1 -1
  80. runbooks/remediation/s3_encryption.py +1 -1
  81. runbooks/remediation/s3_remediation.py +1 -1
  82. runbooks/remediation/vpc_remediation.py +475 -0
  83. runbooks/security/__init__.py +3 -1
  84. runbooks/security/compliance_automation.py +632 -0
  85. runbooks/security/report_generator.py +10 -0
  86. runbooks/security/run_script.py +31 -5
  87. runbooks/security/security_baseline_tester.py +169 -30
  88. runbooks/security/security_export.py +477 -0
  89. runbooks/validation/__init__.py +10 -0
  90. runbooks/validation/benchmark.py +484 -0
  91. runbooks/validation/cli.py +356 -0
  92. runbooks/validation/mcp_validator.py +768 -0
  93. runbooks/vpc/__init__.py +38 -0
  94. runbooks/vpc/config.py +212 -0
  95. runbooks/vpc/cost_engine.py +347 -0
  96. runbooks/vpc/heatmap_engine.py +605 -0
  97. runbooks/vpc/manager_interface.py +634 -0
  98. runbooks/vpc/networking_wrapper.py +1260 -0
  99. runbooks/vpc/rich_formatters.py +679 -0
  100. runbooks/vpc/tests/__init__.py +5 -0
  101. runbooks/vpc/tests/conftest.py +356 -0
  102. runbooks/vpc/tests/test_cli_integration.py +530 -0
  103. runbooks/vpc/tests/test_config.py +458 -0
  104. runbooks/vpc/tests/test_cost_engine.py +479 -0
  105. runbooks/vpc/tests/test_networking_wrapper.py +512 -0
  106. {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/METADATA +40 -12
  107. {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/RECORD +111 -50
  108. {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/WHEEL +0 -0
  109. {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/entry_points.txt +0 -0
  110. {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/licenses/LICENSE +0 -0
  111. {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,458 @@
1
+ """
2
+ Tests for VPC Configuration Management
3
+
4
+ Tests the configuration system for VPC networking operations,
5
+ including environment variable handling and validation.
6
+ """
7
+
8
+ import os
9
+ import tempfile
10
+ from unittest.mock import Mock, patch
11
+
12
+ import pytest
13
+
14
+ from runbooks.vpc.config import (
15
+ AWSCostModel,
16
+ OptimizationThresholds,
17
+ RegionalConfiguration,
18
+ VPCNetworkingConfig,
19
+ load_config,
20
+ )
21
+
22
+
23
+ @pytest.mark.unit
24
+ class TestAWSCostModel:
25
+ """Test AWS Cost Model configuration."""
26
+
27
+ def test_default_cost_model(self):
28
+ """Test default AWS cost model values."""
29
+ cost_model = AWSCostModel()
30
+
31
+ # NAT Gateway pricing defaults
32
+ assert cost_model.nat_gateway_hourly == 0.045
33
+ assert cost_model.nat_gateway_monthly == 45.0
34
+ assert cost_model.nat_gateway_data_processing == 0.045
35
+
36
+ # Transit Gateway pricing defaults
37
+ assert cost_model.transit_gateway_hourly == 0.05
38
+ assert cost_model.transit_gateway_monthly == 36.50
39
+ assert cost_model.transit_gateway_attachment == 0.05
40
+ assert cost_model.transit_gateway_data_processing == 0.02
41
+
42
+ # VPC Endpoint pricing defaults
43
+ assert cost_model.vpc_endpoint_interface_hourly == 0.01
44
+ assert cost_model.vpc_endpoint_interface_monthly == 10.0
45
+ assert cost_model.vpc_endpoint_gateway == 0.0
46
+ assert cost_model.vpc_endpoint_data_processing == 0.01
47
+
48
+ # Elastic IP pricing defaults
49
+ assert cost_model.elastic_ip_idle_hourly == 0.005
50
+ assert cost_model.elastic_ip_idle_monthly == 3.60
51
+ assert cost_model.elastic_ip_attached == 0.0
52
+ assert cost_model.elastic_ip_remap == 0.10
53
+
54
+ def test_cost_model_with_environment_variables(self):
55
+ """Test cost model with custom environment variables."""
56
+ env_vars = {
57
+ "AWS_NAT_GATEWAY_HOURLY": "0.050",
58
+ "AWS_NAT_GATEWAY_MONTHLY": "50.0",
59
+ "AWS_VPC_ENDPOINT_INTERFACE_MONTHLY": "12.0",
60
+ "AWS_ELASTIC_IP_IDLE_MONTHLY": "4.0",
61
+ }
62
+
63
+ with patch.dict(os.environ, env_vars):
64
+ cost_model = AWSCostModel()
65
+
66
+ assert cost_model.nat_gateway_hourly == 0.050
67
+ assert cost_model.nat_gateway_monthly == 50.0
68
+ assert cost_model.vpc_endpoint_interface_monthly == 12.0
69
+ assert cost_model.elastic_ip_idle_monthly == 4.0
70
+
71
+ def test_cost_model_data_transfer_pricing(self):
72
+ """Test data transfer pricing configuration."""
73
+ cost_model = AWSCostModel()
74
+
75
+ assert cost_model.data_transfer_inter_az == 0.01
76
+ assert cost_model.data_transfer_inter_region == 0.02
77
+ assert cost_model.data_transfer_internet_out == 0.09
78
+ assert cost_model.data_transfer_s3_same_region == 0.0 # Always free
79
+
80
+
81
+ @pytest.mark.unit
82
+ class TestOptimizationThresholds:
83
+ """Test optimization thresholds configuration."""
84
+
85
+ def test_default_optimization_thresholds(self):
86
+ """Test default optimization threshold values."""
87
+ thresholds = OptimizationThresholds()
88
+
89
+ # Usage thresholds
90
+ assert thresholds.idle_connection_threshold == 10
91
+ assert thresholds.low_usage_gb_threshold == 100.0
92
+ assert thresholds.low_connection_threshold == 100
93
+
94
+ # Cost thresholds
95
+ assert thresholds.high_cost_threshold == 100.0
96
+ assert thresholds.critical_cost_threshold == 500.0
97
+
98
+ # Optimization targets
99
+ assert thresholds.target_reduction_percent == 30.0
100
+
101
+ # Enterprise thresholds
102
+ assert thresholds.cost_approval_threshold == 1000.0
103
+ assert thresholds.performance_baseline_threshold == 2.0
104
+
105
+ def test_optimization_thresholds_with_environment(self):
106
+ """Test optimization thresholds with environment variables."""
107
+ env_vars = {
108
+ "IDLE_CONNECTION_THRESHOLD": "20",
109
+ "LOW_USAGE_GB_THRESHOLD": "150.0",
110
+ "TARGET_REDUCTION_PERCENT": "40.0",
111
+ "COST_APPROVAL_THRESHOLD": "2000.0",
112
+ "PERFORMANCE_BASELINE_THRESHOLD": "1.5",
113
+ }
114
+
115
+ with patch.dict(os.environ, env_vars):
116
+ thresholds = OptimizationThresholds()
117
+
118
+ assert thresholds.idle_connection_threshold == 20
119
+ assert thresholds.low_usage_gb_threshold == 150.0
120
+ assert thresholds.target_reduction_percent == 40.0
121
+ assert thresholds.cost_approval_threshold == 2000.0
122
+ assert thresholds.performance_baseline_threshold == 1.5
123
+
124
+ def test_threshold_validation_logic(self):
125
+ """Test threshold validation and logic."""
126
+ thresholds = OptimizationThresholds()
127
+
128
+ # Test threshold relationships
129
+ assert thresholds.idle_connection_threshold < thresholds.low_connection_threshold
130
+ assert thresholds.high_cost_threshold < thresholds.critical_cost_threshold
131
+ assert 0 < thresholds.target_reduction_percent < 100
132
+
133
+
134
+ @pytest.mark.unit
135
+ class TestRegionalConfiguration:
136
+ """Test regional configuration settings."""
137
+
138
+ def test_default_regional_configuration(self):
139
+ """Test default regional configuration."""
140
+ regional = RegionalConfiguration()
141
+
142
+ # Default regions
143
+ expected_regions = [
144
+ "us-east-1",
145
+ "us-west-2",
146
+ "us-west-1",
147
+ "eu-west-1",
148
+ "eu-central-1",
149
+ "eu-west-2",
150
+ "ap-southeast-1",
151
+ "ap-southeast-2",
152
+ "ap-northeast-1",
153
+ ]
154
+ assert regional.default_regions == expected_regions
155
+
156
+ # Regional multipliers
157
+ assert "us-east-1" in regional.regional_multipliers
158
+ assert "eu-west-1" in regional.regional_multipliers
159
+ assert "ap-southeast-1" in regional.regional_multipliers
160
+
161
+ # Validate multiplier values are positive
162
+ for region, multiplier in regional.regional_multipliers.items():
163
+ assert multiplier > 0, f"Invalid multiplier for {region}: {multiplier}"
164
+
165
+ def test_regional_multipliers_with_environment(self):
166
+ """Test regional multipliers with environment variables."""
167
+ env_vars = {
168
+ "COST_MULTIPLIER_US_EAST_1": "2.0",
169
+ "COST_MULTIPLIER_EU_WEST_1": "1.5",
170
+ "COST_MULTIPLIER_AP_SOUTHEAST_1": "1.8",
171
+ }
172
+
173
+ with patch.dict(os.environ, env_vars):
174
+ regional = RegionalConfiguration()
175
+
176
+ assert regional.regional_multipliers["us-east-1"] == 2.0
177
+ assert regional.regional_multipliers["eu-west-1"] == 1.5
178
+ assert regional.regional_multipliers["ap-southeast-1"] == 1.8
179
+
180
+ def test_regional_configuration_coverage(self):
181
+ """Test that regional configuration covers major AWS regions."""
182
+ regional = RegionalConfiguration()
183
+
184
+ # Check major regions are covered
185
+ major_regions = ["us-east-1", "us-west-2", "eu-west-1", "ap-southeast-1"]
186
+ for region in major_regions:
187
+ assert region in regional.default_regions
188
+ assert region in regional.regional_multipliers
189
+
190
+
191
+ @pytest.mark.unit
192
+ class TestVPCNetworkingConfig:
193
+ """Test main VPC networking configuration."""
194
+
195
+ def test_default_vpc_networking_config(self):
196
+ """Test default VPC networking configuration."""
197
+ config = VPCNetworkingConfig()
198
+
199
+ # AWS configuration
200
+ assert config.default_region == "us-east-1"
201
+ assert config.billing_profile is None
202
+ assert config.centralized_ops_profile is None
203
+ assert config.single_account_profile is None
204
+ assert config.management_profile is None
205
+
206
+ # Analysis configuration
207
+ assert config.default_analysis_days == 30
208
+ assert config.forecast_days == 90
209
+
210
+ # Output configuration
211
+ assert config.default_output_format == "rich"
212
+ assert str(config.default_output_dir) in ["./exports", "exports"]
213
+
214
+ # Enterprise configuration
215
+ assert config.enable_cost_approval_workflow is True
216
+ assert config.enable_mcp_validation is False
217
+
218
+ # Component configurations
219
+ assert isinstance(config.cost_model, AWSCostModel)
220
+ assert isinstance(config.thresholds, OptimizationThresholds)
221
+ assert isinstance(config.regional, RegionalConfiguration)
222
+
223
+ def test_vpc_config_with_environment_variables(self):
224
+ """Test VPC configuration with environment variables."""
225
+ env_vars = {
226
+ "AWS_DEFAULT_REGION": "us-west-2",
227
+ "BILLING_PROFILE": "test-billing-profile",
228
+ "CENTRALIZED_OPS_PROFILE": "test-ops-profile",
229
+ "SINGLE_ACCOUNT_PROFILE": "test-single-profile",
230
+ "MANAGEMENT_PROFILE": "test-mgmt-profile",
231
+ "DEFAULT_ANALYSIS_DAYS": "45",
232
+ "FORECAST_DAYS": "120",
233
+ "OUTPUT_FORMAT": "json",
234
+ "OUTPUT_DIR": "/tmp/vpc-exports",
235
+ "ENABLE_COST_APPROVAL_WORKFLOW": "false",
236
+ "ENABLE_MCP_VALIDATION": "true",
237
+ }
238
+
239
+ with patch.dict(os.environ, env_vars):
240
+ config = VPCNetworkingConfig()
241
+
242
+ assert config.default_region == "us-west-2"
243
+ assert config.billing_profile == "test-billing-profile"
244
+ assert config.centralized_ops_profile == "test-ops-profile"
245
+ assert config.single_account_profile == "test-single-profile"
246
+ assert config.management_profile == "test-mgmt-profile"
247
+ assert config.default_analysis_days == 45
248
+ assert config.forecast_days == 120
249
+ assert config.default_output_format == "json"
250
+ assert str(config.default_output_dir) == "/tmp/vpc-exports"
251
+ assert config.enable_cost_approval_workflow is False
252
+ assert config.enable_mcp_validation is True
253
+
254
+ def test_cost_approval_required_method(self):
255
+ """Test cost approval required logic."""
256
+ config = VPCNetworkingConfig()
257
+
258
+ # Test below threshold
259
+ assert not config.get_cost_approval_required(500.0)
260
+
261
+ # Test above threshold
262
+ assert config.get_cost_approval_required(1500.0)
263
+
264
+ # Test with disabled approval workflow
265
+ config.enable_cost_approval_workflow = False
266
+ assert not config.get_cost_approval_required(1500.0)
267
+
268
+ def test_performance_acceptable_method(self):
269
+ """Test performance acceptable logic."""
270
+ config = VPCNetworkingConfig()
271
+
272
+ # Test acceptable performance
273
+ assert config.get_performance_acceptable(1.5)
274
+
275
+ # Test unacceptable performance
276
+ assert not config.get_performance_acceptable(3.0)
277
+
278
+ # Test exact threshold
279
+ assert config.get_performance_acceptable(2.0)
280
+
281
+ def test_regional_multiplier_method(self):
282
+ """Test regional multiplier retrieval."""
283
+ config = VPCNetworkingConfig()
284
+
285
+ # Test known region
286
+ multiplier = config.get_regional_multiplier("us-east-1")
287
+ assert multiplier > 0
288
+
289
+ # Test unknown region (should return default)
290
+ multiplier = config.get_regional_multiplier("unknown-region")
291
+ assert multiplier == 1.0
292
+
293
+ def test_config_component_relationships(self):
294
+ """Test relationships between configuration components."""
295
+ config = VPCNetworkingConfig()
296
+
297
+ # Validate cost model thresholds align with optimization thresholds
298
+ assert config.cost_model.nat_gateway_monthly == 45.0
299
+ assert config.thresholds.cost_approval_threshold == 1000.0
300
+
301
+ # Validate that approval threshold is reasonable compared to costs
302
+ monthly_nat_cost = config.cost_model.nat_gateway_monthly
303
+ approval_threshold = config.thresholds.cost_approval_threshold
304
+ assert approval_threshold > monthly_nat_cost * 10 # Should require many resources
305
+
306
+
307
+ @pytest.mark.unit
308
+ class TestLoadConfig:
309
+ """Test configuration loading function."""
310
+
311
+ def test_load_default_config(self):
312
+ """Test loading default configuration."""
313
+ config = load_config()
314
+
315
+ assert isinstance(config, VPCNetworkingConfig)
316
+ assert isinstance(config.cost_model, AWSCostModel)
317
+ assert isinstance(config.thresholds, OptimizationThresholds)
318
+ assert isinstance(config.regional, RegionalConfiguration)
319
+
320
+ def test_load_config_validation_success(self):
321
+ """Test configuration validation success."""
322
+ env_vars = {"BILLING_PROFILE": "test-billing-profile", "ENABLE_COST_APPROVAL_WORKFLOW": "true"}
323
+
324
+ with patch.dict(os.environ, env_vars):
325
+ config = load_config()
326
+
327
+ assert config.billing_profile == "test-billing-profile"
328
+ assert config.enable_cost_approval_workflow is True
329
+
330
+ def test_load_config_validation_failure(self):
331
+ """Test configuration validation failure."""
332
+ env_vars = {
333
+ "ENABLE_COST_APPROVAL_WORKFLOW": "true",
334
+ "BILLING_PROFILE": "", # Empty billing profile
335
+ }
336
+
337
+ with patch.dict(os.environ, env_vars, clear=True):
338
+ with pytest.raises(ValueError, match="BILLING_PROFILE required"):
339
+ load_config()
340
+
341
+ def test_load_config_with_file_parameter(self):
342
+ """Test loading configuration with config file parameter."""
343
+ # TODO: Implement when config file support is added
344
+ config = load_config(config_file=None)
345
+
346
+ # Should work with None (uses environment variables)
347
+ assert isinstance(config, VPCNetworkingConfig)
348
+
349
+ @pytest.mark.integration
350
+ def test_configuration_environment_isolation(self):
351
+ """Test that configuration properly isolates environment variables."""
352
+ # Set initial environment
353
+ initial_env = {"AWS_DEFAULT_REGION": "us-east-1", "BILLING_PROFILE": "initial-profile"}
354
+
355
+ with patch.dict(os.environ, initial_env, clear=True):
356
+ config1 = load_config()
357
+ assert config1.default_region == "us-east-1"
358
+ assert config1.billing_profile == "initial-profile"
359
+
360
+ # Change environment
361
+ changed_env = {"AWS_DEFAULT_REGION": "us-west-2", "BILLING_PROFILE": "changed-profile"}
362
+
363
+ with patch.dict(os.environ, changed_env, clear=True):
364
+ config2 = load_config()
365
+ assert config2.default_region == "us-west-2"
366
+ assert config2.billing_profile == "changed-profile"
367
+
368
+ # Confirm original config unchanged
369
+ assert config1.default_region == "us-east-1"
370
+ assert config1.billing_profile == "initial-profile"
371
+
372
+
373
+ @pytest.mark.performance
374
+ class TestConfigurationPerformance:
375
+ """Test configuration loading performance."""
376
+
377
+ def test_config_loading_performance(self):
378
+ """Test configuration loading performance benchmark."""
379
+ import time
380
+
381
+ start_time = time.time()
382
+
383
+ # Load configuration multiple times
384
+ for _ in range(10):
385
+ config = load_config()
386
+
387
+ execution_time = time.time() - start_time
388
+
389
+ # Configuration loading should be very fast
390
+ assert execution_time < 1.0, f"Configuration loading too slow: {execution_time:.2f}s"
391
+
392
+ def test_config_memory_usage(self):
393
+ """Test configuration memory efficiency."""
394
+ import sys
395
+
396
+ # Get initial memory usage
397
+ initial_size = sys.getsizeof({})
398
+
399
+ config = load_config()
400
+
401
+ # Configuration should not use excessive memory
402
+ config_size = sys.getsizeof(config.__dict__)
403
+
404
+ # Should be reasonable for configuration data
405
+ assert config_size < 10000, f"Configuration using too much memory: {config_size} bytes"
406
+
407
+
408
+ @pytest.mark.security
409
+ class TestConfigurationSecurity:
410
+ """Test configuration security aspects."""
411
+
412
+ def test_no_credentials_in_config(self):
413
+ """Test that configuration doesn't expose credentials."""
414
+ config = load_config()
415
+
416
+ # Convert config to string representation
417
+ config_str = str(config.__dict__)
418
+
419
+ # Check for common credential patterns
420
+ sensitive_patterns = ["AKIA", "SECRET", "TOKEN", "PASSWORD", "KEY"]
421
+ for pattern in sensitive_patterns:
422
+ assert pattern not in config_str.upper(), f"Potentially sensitive pattern '{pattern}' found in config"
423
+
424
+ def test_profile_name_validation(self):
425
+ """Test profile name validation and sanitization."""
426
+ # Test with potentially dangerous profile names
427
+ dangerous_profiles = ["profile; rm -rf /", "profile && malicious_command", "profile | grep secrets"]
428
+
429
+ for dangerous_profile in dangerous_profiles:
430
+ env_vars = {"BILLING_PROFILE": dangerous_profile}
431
+
432
+ with patch.dict(os.environ, env_vars):
433
+ config = load_config()
434
+
435
+ # Profile name should be stored as-is (validation happens at usage)
436
+ assert config.billing_profile == dangerous_profile
437
+
438
+ def test_environment_variable_precedence(self):
439
+ """Test environment variable precedence and override behavior."""
440
+ # Set base environment
441
+ base_env = {"AWS_DEFAULT_REGION": "us-east-1", "BILLING_PROFILE": "base-profile"}
442
+
443
+ with patch.dict(os.environ, base_env):
444
+ config = load_config()
445
+
446
+ # Verify base values
447
+ assert config.default_region == "us-east-1"
448
+ assert config.billing_profile == "base-profile"
449
+
450
+ # Override with new values
451
+ override_env = {"AWS_DEFAULT_REGION": "eu-west-1", "BILLING_PROFILE": "override-profile"}
452
+
453
+ with patch.dict(os.environ, override_env):
454
+ config_override = load_config()
455
+
456
+ # Verify override values
457
+ assert config_override.default_region == "eu-west-1"
458
+ assert config_override.billing_profile == "override-profile"