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 .github/workflows/python-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.11", "3.12"]
python-version: ["3.12"]

steps:
- uses: actions/checkout@v4
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

*A serverless framework that scans your AWS accounts for AI/ML security misconfigurations and produces an interactive, shareable report.*

[![License: MIT-0](https://img.shields.io/badge/License-MIT--0-yellow.svg)](https://opensource.org/licenses/MIT-0) [![Python 3.11+](https://img.shields.io/badge/Python-3.11+-blue.svg)](https://www.python.org/downloads/) [![AWS SAM](https://img.shields.io/badge/AWS-SAM-orange.svg)](https://aws.amazon.com/serverless/sam/)
[![License: MIT-0](https://img.shields.io/badge/License-MIT--0-yellow.svg)](https://opensource.org/licenses/MIT-0) [![Python 3.12](https://img.shields.io/badge/Python-3.12-blue.svg)](https://www.python.org/downloads/) [![AWS SAM](https://img.shields.io/badge/AWS-SAM-orange.svg)](https://aws.amazon.com/serverless/sam/)

**Open-source automated security scanner for generative AI and machine learning workloads on AWS.** Core checks for Amazon Bedrock, Amazon SageMaker AI, and Amazon Bedrock AgentCore are built on the [AWS Well-Architected Framework — Generative AI Lens](https://docs.aws.amazon.com/wellarchitected/latest/generative-ai-lens/generative-ai-lens.html). An optional Financial Services GenAI risk module adds 64 checks aligned to the [AWS User Guide to Governance, Risk, and Compliance for Responsible AI Adoption within Financial Services Industries](https://d1.awsstatic.com/onedam/marketing-channels/website/aws/en_US/whitepapers/compliance/AWS-User-Guide-Governance-Risk-Compliance-for-Responsible-AI-Adoption-Financial-Services.pdf). See the [AWS Security Blog announcement](https://aws.amazon.com/blogs/security/introducing-the-updated-aws-user-guide-to-governance-risk-and-compliance-for-responsible-ai-adoption/) for context on the updated guide.

Expand Down Expand Up @@ -131,7 +131,7 @@ This tool operates within the [AWS Shared Responsibility Model](https://aws.amaz

## Prerequisites

- Python 3.12+ — [Install Python](https://www.python.org/downloads/)
- Python 3.12 — [Install Python](https://www.python.org/downloads/)
- AWS SAM CLI — [Install the AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html)
- Docker (optional) — [Install Docker](https://hub.docker.com/search/?type=edition&offering=community) — Only required for local development

Expand Down Expand Up @@ -405,7 +405,7 @@ GitHub Actions workflows run automatically on pull requests and selected pushes:
| Workflow | Trigger | What It Checks |
|----------|---------|----------------|
| **Python Code Quality** | PR | `ruff check` and `ruff format --check` on changed Python files |
| **AI/ML Security Assessment Tests** | PR, push to `main`/`develop` | Runs the `pytest` suite (assessment functions and report pipeline) on Python 3.11 and 3.12 |
| **AI/ML Security Assessment Tests** | PR, push to `main`/`develop` | Runs the `pytest` suite (assessment functions and report pipeline) on Python 3.12 |
| **CloudFormation Lint** | PR | Validates deployment and SAM templates with `cfn-lint` |
| **SAM Validate & Build** | PR | `sam validate --lint` and `sam build` on SAM templates |
| **ASH Security Scan** | PR | Scans for secrets, dependency vulnerabilities, and IaC misconfigurations |
Expand Down
Binary file modified sample-reports/dashboard-overview-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified sample-reports/dashboard-overview-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified sample-reports/findings-table.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified sample-reports/multi-account-summary.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
77 changes: 77 additions & 0 deletions sample-reports/scripts/capture_screenshots.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
python sample-reports/scripts/capture_screenshots.py
"""

import re
import sys
from pathlib import Path
from playwright.sync_api import sync_playwright
Expand All @@ -32,6 +33,19 @@
JPEG_QUALITY = 85 # Balance between quality and file size
PNG_OPTIMIZE = True

# Well-known AWS documentation example account IDs (not real accounts). Real
# account IDs discovered in the sample reports are consistently remapped to
# these placeholders before screenshots are captured.
# See: https://docs.aws.amazon.com/accounts/latest/reference/manage-acct-identifiers.html
ANONYMIZED_ACCOUNT_IDS = [
"111122223333",
"444455556666",
"777788889999",
"123456789012",
"555555555555",
"666677778888",
]

# Screenshots to capture
SCREENSHOTS = [
{
Expand Down Expand Up @@ -80,6 +94,64 @@
]


def anonymize_account_ids(html_files: list) -> None:
"""
Replace real 12-digit AWS account IDs in the given HTML files with
well-known example account IDs, in place.

Each distinct real account ID is mapped to a stable placeholder so that
filtering and grouping in the reports keep working. The mapping is shared
across all provided files, so an account that appears in multiple reports
is anonymized to the same placeholder everywhere.

Args:
html_files: List of Path objects pointing to HTML report files.
"""
print("\n Anonymizing account IDs...")

# Collect every distinct 12-digit account ID across all files first so the
# placeholder assignment is deterministic regardless of processing order.
account_id_pattern = re.compile(r"\b\d{12}\b")
discovered = []
file_contents = {}
for html_file in html_files:
if not html_file.exists():
print(f" WARNING: {html_file} not found, skipping...")
continue
content = html_file.read_text(encoding="utf-8")
file_contents[html_file] = content
for account_id in account_id_pattern.findall(content):
if account_id not in discovered:
discovered.append(account_id)

if not discovered:
print(" No account IDs found to anonymize.")
return

if len(discovered) > len(ANONYMIZED_ACCOUNT_IDS):
print(
f" ERROR: Found {len(discovered)} distinct account IDs but only "
f"{len(ANONYMIZED_ACCOUNT_IDS)} placeholders are defined. "
"Add more entries to ANONYMIZED_ACCOUNT_IDS."
)
sys.exit(1)

mapping = dict(zip(discovered, ANONYMIZED_ACCOUNT_IDS))
for real_id, placeholder in mapping.items():
print(f" {real_id} -> {placeholder}")

# Apply the mapping to each file. Replace via the same word-boundary regex
# to avoid touching digits that happen to embed a 12-digit run.
def _replace(match: "re.Match") -> str:
return mapping.get(match.group(0), match.group(0))

for html_file, content in file_contents.items():
updated = account_id_pattern.sub(_replace, content)
if updated != content:
html_file.write_text(updated, encoding="utf-8")
print(f" Updated: {html_file.name}")


def optimize_png(image_path: Path, max_size_kb: int = 300) -> None:
"""
Optimize PNG image to reduce file size while maintaining quality.
Expand Down Expand Up @@ -190,6 +262,11 @@ def main():
print(f" Viewport size: {VIEWPORT_WIDTH}x{VIEWPORT_HEIGHT}")
print(f" Target: {len(SCREENSHOTS)} screenshots")

# Anonymize account IDs in the source HTML reports before capturing so the
# screenshots (and the reports themselves) never expose real account IDs.
report_files = sorted({SAMPLE_REPORTS_DIR / cfg["file"] for cfg in SCREENSHOTS})
anonymize_account_ids(report_files)

try:
with sync_playwright() as p:
# Launch browser
Expand Down
Loading
Loading