Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ Both deployment modes support scanning multiple AWS regions in parallel via the

Scanning uses a Step Functions Map state, so multiple regions execute in parallel with no additional time cost. Services unavailable in a region produce an informational N/A finding.

The HTML report includes a Region column, filter dropdown, and "Risk by Region" summary.
The HTML report includes a Region column, filter dropdown, and "Risk by Region / Scope" summary.

> **Upgrading an existing deployment?** See [Troubleshooting](docs/TROUBLESHOOTING.md#9-upgrading-an-existing-deployment-to-multi-region) — it's a simple stack parameter update with no teardown.

Expand Down

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""
Drift-guard for the FinServ severity methodology (REQ-6 / severity-register.md).
Drift-guard for the FinServ severity methodology
(REQ-6 / SECURITY_CHECKS_FINSERV_SEVERITY_REGISTER.md).

Asserts that:
1. The Likelihood x Impact matrix helper matches the documented table (methodology §3.3).
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,16 @@ def test_generate_viewable_report(self):
self.assertIn("Methodology", content)
self.assertIn("Severity Legend", content)
self.assertIn("sortable", content)
self.assertIn("Failed High, Medium, and Low findings", content)
self.assertIn('<option value="failed" selected>Failed</option>', content)
self.assertIn('class="single-account-report"', content)
self.assertIn("Details and remediation", content)
self.assertLess(content.index('id="risk"'), content.index('id="findings"'))
self.assertIn(
"const status = this.hasAttribute('data-filter-status')",
content,
)
self.assertNotIn("this.dataset.filterStatus || 'failed'", content)

def test_generate_multi_account_report(self):
"""Test multi-account report generation using shared template directly"""
Expand Down Expand Up @@ -348,13 +358,13 @@ def test_finserv_renders_when_present(self):
}
html = generate_html_report(data)
self.assertIn('id="finserv"', html)
self.assertIn('id="finservTable"', html)
self.assertIn('id="findingsTable"', html)
self.assertNotIn('id="finservTable"', html)
self.assertIn('<option value="finserv">', html)
self.assertIn("FS-01", html)
self.assertIn('data-service="finserv"', html)
self.assertIn('id="finservRegionFilter"', html)
self.assertIn('<option value="region-a">region-a</option>', html)
self.assertIn('<option value="region-b">region-b</option>', html)
self.assertIn('data-filter-service="finserv"', html)
self.assertIn("View failed findings", html)
self.assertIn('data-scope-service="finserv"', html)
self.assertIn('class="scope-industry"', html)
self.assertIn('class="scope-chip industry-chip"', html)
Expand Down Expand Up @@ -407,7 +417,7 @@ def test_agentic_security_renders_when_present(self):
"Finding": "Bedrock Agent Guardrail Association",
"Finding_Details": "Agent has a guardrail.",
"Resolution": "No action required.",
"Reference": "https://docs.aws.amazon.com/bedrock/latest/userguide/agents-guardrails.html",
"Reference": "https://docs.aws.amazon.com/bedrock/latest/userguide/guardrails-use.html",
"Severity": "High",
"Status": "Passed",
"Region": "us-east-1",
Expand All @@ -429,12 +439,14 @@ def test_agentic_security_renders_when_present(self):
html = generate_html_report(data)

self.assertIn('id="agentic"', html)
self.assertIn('id="agenticTable"', html)
self.assertIn('id="findingsTable"', html)
self.assertNotIn('id="agenticTable"', html)
self.assertIn('<option value="agentic">Agentic AI Security</option>', html)
self.assertIn("<h3>By Lens</h3>", html)
self.assertIn('class="nav-section lens-nav"', html)
self.assertIn("AG-01", html)
self.assertIn('data-service="agentic"', html)
self.assertIn('data-filter-service="agentic"', html)
self.assertIn("Agentic AI Security Findings", html)
self.assertIn(
"wellarchitected/latest/agentic-ai-lens/agentic-ai-lens.html", html
Expand Down Expand Up @@ -466,6 +478,74 @@ def test_agentic_security_omitted_when_absent(self):
"wellarchitected/latest/agentic-ai-lens/agentic-ai-lens.html", html
)

def test_region_risk_includes_global_scope_card(self):
"""Global failed findings render in risk by region / scope without inflating regions."""
all_findings = [
{
"account_id": "123456789012",
"check_id": "BR-01",
"finding": "Regional Bedrock Check",
"details": "No regional issue.",
"resolution": "No action required.",
"reference": "https://example.com",
"severity": "High",
"status": "Passed",
"region": "us-east-1",
"_service": "bedrock",
},
{
"account_id": "123456789012",
"check_id": "BR-03",
"finding": "Marketplace Subscription Access Check",
"details": "Overly permissive marketplace subscription access.",
"resolution": "Restrict subscription access.",
"reference": "https://example.com",
"severity": "High",
"status": "Failed",
"region": "Global",
"_service": "bedrock",
},
{
"account_id": "123456789012",
"check_id": "AC-09",
"finding": "AgentCore Service-Linked Role Missing",
"details": "Service-linked role is missing.",
"resolution": "Allow service-linked role creation.",
"reference": "https://example.com",
"severity": "Medium",
"status": "Failed",
"region": "Global",
"_service": "agentcore",
},
]

html = generate_report_direct(
all_findings=all_findings,
service_findings={
"bedrock": all_findings[:2],
"agentcore": [all_findings[2]],
},
service_stats={
"bedrock": {"passed": 1, "failed": 1, "na": 0},
"agentcore": {"passed": 0, "failed": 1, "na": 0},
},
mode="single",
account_id="123456789012",
regions=["eu-west-1", "us-east-1", "us-west-2"],
)

self.assertIn("Risk by Region / Scope", html)
self.assertIn('>eu-west-1</div><div class="metric-value">0</div>', html)
self.assertIn('>us-east-1</div><div class="metric-value">0</div>', html)
self.assertIn('>us-west-2</div><div class="metric-value">0</div>', html)
self.assertIn('>Global</div><div class="metric-value">2</div>', html)
self.assertIn(
'<span style="color: var(--danger);">1 High</span> · '
'<span style="color: var(--warning);">1 Med</span> · '
'<span style="color: var(--accent);">0 Low</span>',
html,
)

def tearDown(self):
"""Clean up test files after running tests"""
pass
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1852,7 +1852,7 @@ def check_sagemaker_monitoring_network_isolation(region: str = "") -> Dict[str,
finding_name="SageMaker Monitoring Network Isolation Disabled",
finding_details=f"Monitoring schedule '{schedule['name']}' does not have network isolation enabled. Monitoring jobs can make outbound network calls.",
resolution="Enable network isolation in the monitoring job definition NetworkConfig to prevent outbound network access.",
reference="https://docs.aws.amazon.com/sagemaker/latest/dg/model-monitor-network-isolation.html",
reference="https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_MonitoringNetworkConfig.html",
severity="Medium",
status="Failed",
region=region,
Expand All @@ -1867,7 +1867,7 @@ def check_sagemaker_monitoring_network_isolation(region: str = "") -> Dict[str,
finding_name="SageMaker Monitoring Network Isolation Check",
finding_details=f"All {len(schedules_with_isolation)} monitoring schedules have network isolation enabled",
resolution="No action required",
reference="https://docs.aws.amazon.com/sagemaker/latest/dg/model-monitor-network-isolation.html",
reference="https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_MonitoringNetworkConfig.html",
severity="Medium",
status="Passed",
region=region,
Expand All @@ -1881,7 +1881,7 @@ def check_sagemaker_monitoring_network_isolation(region: str = "") -> Dict[str,
finding_name="SageMaker Monitoring Network Isolation Check",
finding_details="No monitoring schedules found",
resolution="No action required",
reference="https://docs.aws.amazon.com/sagemaker/latest/dg/model-monitor-network-isolation.html",
reference="https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_MonitoringNetworkConfig.html",
severity="Informational",
status="N/A",
region=region,
Expand Down Expand Up @@ -2902,7 +2902,7 @@ def check_sagemaker_automl_network_isolation(region: str = "") -> Dict[str, Any]
finding_name="SageMaker AutoML Job Network Isolation Disabled",
finding_details=f"AutoML job '{job['name']}' does not have inter-container traffic encryption enabled. Data transmitted between containers during training is not encrypted.",
resolution="Enable EnableInterContainerTrafficEncryption in AutoMLJobConfig.SecurityConfig when creating AutoML jobs.",
reference="https://docs.aws.amazon.com/sagemaker/latest/dg/autopilot-security.html",
reference="https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_AutoMLSecurityConfig.html",
severity="Medium",
status="Failed",
region=region,
Expand All @@ -2916,7 +2916,7 @@ def check_sagemaker_automl_network_isolation(region: str = "") -> Dict[str, Any]
finding_name="SageMaker AutoML Job Network Isolation Summary",
finding_details=f"Found {len(jobs_without_isolation)} total AutoML jobs without network isolation (showing first 15)",
resolution="Review all AutoML jobs and enable inter-container traffic encryption",
reference="https://docs.aws.amazon.com/sagemaker/latest/dg/autopilot-security.html",
reference="https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_AutoMLSecurityConfig.html",
severity="Medium",
status="Failed",
region=region,
Expand All @@ -2931,7 +2931,7 @@ def check_sagemaker_automl_network_isolation(region: str = "") -> Dict[str, Any]
finding_name="SageMaker AutoML Job Network Isolation Check",
finding_details=f"All {len(jobs_with_isolation)} AutoML jobs have inter-container encryption enabled",
resolution="No action required",
reference="https://docs.aws.amazon.com/sagemaker/latest/dg/autopilot-security.html",
reference="https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_AutoMLSecurityConfig.html",
severity="Medium",
status="Passed",
region=region,
Expand All @@ -2945,7 +2945,7 @@ def check_sagemaker_automl_network_isolation(region: str = "") -> Dict[str, Any]
finding_name="SageMaker AutoML Job Network Isolation Check",
finding_details="No AutoML jobs found",
resolution="No action required",
reference="https://docs.aws.amazon.com/sagemaker/latest/dg/autopilot-security.html",
reference="https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_AutoMLSecurityConfig.html",
severity="Informational",
status="N/A",
region=region,
Expand Down
8 changes: 6 additions & 2 deletions docs/DEVELOPER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ The AI/ML Security Assessment Framework is a serverless, multi-account security
sample-aiml-security-assessment/
├── aiml-security-assessment/
│ ├── functions/security/
│ │ ├── bedrock_assessments/ # Bedrock security checks (14)
│ │ ├── bedrock_assessments/ # Bedrock security checks (32)
│ │ ├── sagemaker_assessments/ # SageMaker security checks (25)
│ │ ├── agentcore_assessments/ # AgentCore security checks (13)
│ │ ├── finserv_assessments/ # Optional Financial Services GenAI risk checks (64)
Expand All @@ -116,8 +116,12 @@ sample-aiml-security-assessment/
├── deployment/ # AWS CloudFormation templates
├── docs/ # Documentation
│ ├── DEVELOPER_GUIDE.md # This guide
│ ├── SECURITY_CHECKS.md # Security checks reference
│ ├── SECURITY_CHECKS.md # Security checks reference (core + Agentic)
│ ├── SECURITY_CHECKS_FINSERV.md # FinServ GenAI risk checks reference
│ ├── SECURITY_CHECKS_FINSERV_SEVERITY_METHODOLOGY.md # FinServ severity model
│ ├── SECURITY_CHECKS_FINSERV_SEVERITY_REGISTER.md # FinServ per-finding severities
│ ├── TROUBLESHOOTING.md # Troubleshooting guide
│ ├── CLEANUP.md # Resource removal guide
│ ├── diagrams/ # Architecture diagrams
│ └── icons/ # AWS service icons
├── sample-reports/ # Sample assessment reports
Expand Down
6 changes: 3 additions & 3 deletions docs/SECURITY_CHECKS.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ Each security check has a unique identifier with a service prefix:

### SM-04: Amazon GuardDuty Integration

- **Severity:** Medium
- **Severity:** High
- **Description:** Verifies Amazon GuardDuty runtime threat detection is enabled.

### SM-05: MLOps Features
Expand Down Expand Up @@ -124,7 +124,7 @@ Each security check has a unique identifier with a service prefix:

### SM-11: Model Network Isolation

- **Severity:** Medium
- **Severity:** High
- **AWS Security Hub Control:** SageMaker.4
- **Description:** Checks inference containers have network isolation.

Expand Down Expand Up @@ -417,7 +417,7 @@ Each security check has a unique identifier with a service prefix:

### AC-07: Memory Encryption

- **Severity:** High
- **Severity:** Medium
- **Description:** Checks agent memory encryption with AWS KMS.

### AC-08: Amazon VPC Endpoints
Expand Down
18 changes: 9 additions & 9 deletions docs/SECURITY_CHECKS_FINSERV.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,14 +198,14 @@ Each FS check maps to one or more FinServ regulatory frameworks (preliminary map

| Framework | Description | Relevant Checks |
|-----------|-------------|-----------------|
| SR 11-7 | Federal Reserve Model Risk Management Guidance | FS-07, FS-10, FS-12 to FS-16, FS-20, FS-21, FS-27 to FS-33, FS-34, FS-39 to FS-42, FS-66, FS-67 |
| FFIEC CAT | Cybersecurity Assessment Tool | All FS checks |
| NYDFS 500 | NY Cybersecurity Regulation | FS-22, FS-43 to FS-46, FS-51 to FS-54, FS-66 |
| PCI-DSS | Payment Card Industry Data Security Standard | FS-22, FS-25, FS-26, FS-43 to FS-46, FS-53, FS-56, FS-67, FS-68 |
| DORA | EU Digital Operational Resilience Act | FS-01 to FS-06, FS-08, FS-11, FS-54, FS-65, FS-68 |
| MAS TRM 9 | Monetary Authority of Singapore Technology Risk Management | FS-07 to FS-11, FS-15, FS-27 to FS-30, FS-32, FS-37, FS-39 to FS-42, FS-66, FS-67 |
| ISO 27001 | Information Security Management | FS-13, FS-14, FS-16, FS-21, FS-33, FS-46, FS-52, FS-63, FS-65 |
| ECOA/Fair Housing | Equal Credit Opportunity Act (US) | FS-39 to FS-42 (advisory — applicability depends on whether the model is used for ECOA-covered credit decisions; confirm with your compliance team) |
| SR 11-7 | Federal Reserve Model Risk Management Guidance | FS-03, FS-04, FS-06 to FS-10, FS-12 to FS-15, FS-20, FS-21, FS-27 to FS-42, FS-47 to FS-50, FS-59 to FS-63, FS-66, FS-67 |
| FFIEC CAT | Cybersecurity Assessment Tool | All FS checks except FS-08, FS-56, FS-66 |
| NYDFS 500 | NY Cybersecurity Regulation | FS-22, FS-24 to FS-26, FS-28 to FS-30, FS-43 to FS-46, FS-51 to FS-54, FS-56, FS-57, FS-66, FS-69 |
| PCI-DSS | Payment Card Industry Data Security Standard | FS-02, FS-22, FS-24 to FS-26, FS-43 to FS-46, FS-53, FS-56, FS-66 to FS-68 |
| DORA | EU Digital Operational Resilience Act | FS-01, FS-02, FS-05, FS-11, FS-16, FS-65, FS-68 |
| MAS TRM 9 | Monetary Authority of Singapore Technology Risk Management | FS-08, FS-10, FS-15, FS-27 to FS-29, FS-32, FS-37, FS-49, FS-62, FS-66, FS-67 |
| ISO 27001 | Information Security Management | FS-12 to FS-14, FS-16, FS-21, FS-33, FS-46, FS-52, FS-63, FS-65 |
| ECOA/Fair Housing | Equal Credit Opportunity Act (US) | FS-39, FS-40 (advisory — applicability depends on whether the model is used for ECOA-covered credit decisions; confirm with your compliance team) |
| OWASP LLM Top 10 | OWASP LLM Application Security | FS-51 to FS-58, FS-68, FS-69 |

> **FS-34 note:** FS-34 (TPRM for FM Providers) is listed above under SR 11-7. Although the
Expand Down Expand Up @@ -1016,7 +1016,7 @@ Each FS check maps to one or more FinServ regulatory frameworks (preliminary map
| Description | Checks for Lambda functions implementing output validation/sanitization before AI responses reach downstream consumers. |
| Detection | Calls `lambda:ListFunctions` and searches for functions with naming patterns indicating output validation (e.g., "output-valid", "sanitiz", "post-process", "response-filter"). Flags if no such functions exist. |
| Remediation | 1. Implement a post-processing Lambda that validates AI model output before it reaches the end user or downstream system. 2. Validate output against expected schema (JSON schema validation for structured responses). 3. Strip or escape any executable content (HTML tags, JavaScript, SQL fragments). 4. Log rejected outputs for security monitoring. |
| Reference | [AWS Well-Architected Security Pillar — Application Security](https://docs.aws.amazon.com/wellarchitected/latest/security-pillar/application-security.html), [Bedrock Prompt Injection Security](https://docs.aws.amazon.com/bedrock/latest/userguide/prompt-injection.html), [Well-Architected FSI Lens — FSISEC14 Monitor AI system outputs for security issues](https://docs.aws.amazon.com/wellarchitected/latest/financial-services-industry-lens/fsisec14.html) |
| Reference | [OWASP LLM05:2025 Improper Output Handling](https://genai.owasp.org/llmrisk/llm052025-improper-output-handling/), [AWS Well-Architected Security Pillar — Application Security](https://docs.aws.amazon.com/wellarchitected/latest/security-pillar/application-security.html), [Bedrock Prompt Injection Security](https://docs.aws.amazon.com/bedrock/latest/userguide/prompt-injection.html), [Well-Architected FSI Lens — FSISEC14 Monitor AI system outputs for security issues](https://docs.aws.amazon.com/wellarchitected/latest/financial-services-industry-lens/fsisec14.html) |

#### FS-56 — XSS Prevention WAF

Expand Down
Loading
Loading