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
61 changes: 61 additions & 0 deletions .claude/skills/connect-gcp/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
name: connect-gcp
description: Connect to a GCP project, validate credentials, and discover what services are in use.
user-invocable: true
---

# Connect GCP

You are helping a semi-technical founder connect Shasta to their Google Cloud project for SOC 2 and ISO 27001 compliance scanning.

## Configuration

Shasta uses `shasta.config.json` in the project root for all settings. Before running any commands, check if this file has `gcp_project_id` set. If not, you'll need to configure it.

## What to do

1. **Check that the GCP SDK extra is installed.** The GCP check modules need the `[gcp]` optional dependencies (`google-auth`, `google-api-python-client`, `google-cloud-storage`). Test with:
```bash
<PYTHON_CMD> -c "import google.auth, googleapiclient, google.cloud.storage; print('gcp deps OK')"
```
If that fails with `ModuleNotFoundError`, install them: `pip install -e ".[gcp]"`

2. **Check if shasta.config.json is configured for GCP.** Read the file. If `gcp_project_id` is empty, ask the user:
- Have you run `gcloud auth application-default login`? If not, tell them to run `! gcloud auth application-default login` first. This sets up Application Default Credentials (ADC) — the same mechanism a service account or Workload Identity would use.
- Run `gcloud config get-value project` to get their current project ID. If they have several, `gcloud projects list` shows all of them.
- What region are their resources in? (e.g., `us-central1`, `us-east1`, `europe-west1`) — default: `us-central1`
- If `company_name` is still empty, ask for it too.

Update `shasta.config.json` with their answers — set `gcp_project_id` and `gcp_region`.

3. **Also detect the correct Python command** if `python_cmd` isn't set. Run `python3 --version` and `python --version` to find which works. Update `python_cmd` in the config.

4. **Validate GCP credentials** by running (substitute the correct python command):
```bash
<PYTHON_CMD> -c "
from shasta.config import get_gcp_client
c = get_gcp_client()
info = c.validate_credentials()
services = c.discover_services()
print(f'GCP Project: {info.project_name} ({info.project_id})')
print(f'Project Number: {info.project_number}')
print(f'Principal: {info.principal}')
print(f'Region: {info.region}')
print(f'Services detected: {services if services else \"none (empty project)\"}')
"
```

5. **Initialize the Shasta database** (if not already done):
```bash
<PYTHON_CMD> -c "from shasta.db.schema import ShastaDB; db = ShastaDB(); db.initialize(); print('Database initialized at data/shasta.db')"
```

6. **Present results** in a clear, friendly format and suggest running `/scan` next.

## Important notes

- **Never ask the user to paste GCP credentials or service account keys into the chat.** Always use `gcloud auth application-default login` or the `GOOGLE_APPLICATION_CREDENTIALS` environment variable pointing at a key file.
- Replace `<PYTHON_CMD>` with whatever works on this machine (`python3`, `python`, or `py -3.12`).
- If credentials fail, guide them through `gcloud auth application-default login` or check that the active project is set with `gcloud config set project <PROJECT_ID>`.
- GCP scanning requires read access. The `roles/viewer` (Project Viewer) basic role is sufficient for all checks; `roles/iam.securityReviewer` covers the IAM-policy checks if Viewer is too broad for the org.
- Some checks (org-policy, audit-log config) require APIs to be enabled — `cloudresourcemanager.googleapis.com`, `serviceusage.googleapis.com`, `compute.googleapis.com`. Missing APIs produce NOT_ASSESSED findings, which is fine.
115 changes: 24 additions & 91 deletions .claude/skills/scan/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: scan
description: Run SOC 2 compliance checks against connected cloud accounts (AWS and/or Azure) and display findings.
description: Run SOC 2 compliance checks against connected cloud accounts (AWS, Azure, and/or GCP) and display findings.
user-invocable: true
---

Expand All @@ -10,13 +10,14 @@ You are running a SOC 2 compliance scan for a semi-technical founder. Explain fi

## What to do

Read `shasta.config.json` for `python_cmd`, `aws_profile`, and `azure_subscription_id`. Use that for all commands (shown as `<PYTHON_CMD>`).
Read `shasta.config.json` for `python_cmd`, `aws_profile`, `azure_subscription_id`, and `gcp_project_id`. Use that for all commands (shown as `<PYTHON_CMD>`).

**Determine which clouds to scan:**
- If `aws_profile` is set (non-empty) → scan AWS
- If `azure_subscription_id` is set (non-empty) → scan Azure
- If both are set → scan both
- If neither is set → tell the user to run `/connect-aws` or `/connect-azure` first
- If `gcp_project_id` is set (non-empty) → scan GCP
- Any combination of the above is scanned together in a single pass
- If none are set → tell the user to run `/connect-aws`, `/connect-azure`, or `/connect-gcp` first

### Check for a recent scan first

Expand All @@ -39,104 +40,36 @@ If a recent scan exists, tell the user and ask if they want to reuse it or run f

### Run fresh scan + generate reports

Construct the Python command based on which clouds are configured. The scanner supports both AWS and Azure simultaneously.
The scanner takes AWS, Azure, and GCP clients together and scans whatever is
passed in a single pass. The snippet below builds only the clients that are
configured, so the same command works for any cloud or combination — no need to
pick a cloud-specific variant.

**For AWS only:**
```bash
<PYTHON_CMD> -c "
import json
from shasta.config import get_aws_client
from shasta.config import load_config, get_aws_client, get_azure_client, get_gcp_client
from shasta.scanner import run_full_scan
from shasta.compliance.mapper import get_control_summary
from shasta.compliance.scorer import calculate_score
from shasta.reports.summary import summarize_scan
from shasta.reports.generator import save_markdown_report, save_html_report
from shasta.db.schema import ShastaDB

client = get_aws_client()
client.validate_credentials()
print('Running full compliance scan (AWS)...')
scan = run_full_scan(client)
db = ShastaDB(); db.initialize(); db.save_scan(scan)

md = save_markdown_report(scan)
html = save_html_report(scan)
print(f'Reports saved: {md} | {html}')

score = calculate_score(scan.findings)
summary = summarize_scan(scan)
summary['score'] = {
'percentage': score.score_percentage,
'grade': score.grade,
'controls_passing': score.passing,
'controls_failing': score.failing,
}
summary['control_summary'] = {
k: {'title': v['title'], 'overall_status': v['overall_status'], 'pass_count': v['pass_count'], 'fail_count': v['fail_count']}
for k, v in get_control_summary(scan.findings).items()
if v['has_automated_checks'] or v['overall_status'] != 'not_assessed'
}
print(json.dumps(summary, indent=2))
"
```

**For Azure only:**
```bash
<PYTHON_CMD> -c "
import json
from shasta.config import get_azure_client
from shasta.scanner import run_full_scan
from shasta.compliance.mapper import get_control_summary
from shasta.compliance.scorer import calculate_score
from shasta.reports.summary import summarize_scan
from shasta.reports.generator import save_markdown_report, save_html_report
from shasta.db.schema import ShastaDB

azure_client = get_azure_client()
azure_client.validate_credentials()
print('Running full compliance scan (Azure)...')
scan = run_full_scan(azure_client=azure_client)
db = ShastaDB(); db.initialize(); db.save_scan(scan)

md = save_markdown_report(scan)
html = save_html_report(scan)
print(f'Reports saved: {md} | {html}')

score = calculate_score(scan.findings)
summary = summarize_scan(scan)
summary['score'] = {
'percentage': score.score_percentage,
'grade': score.grade,
'controls_passing': score.passing,
'controls_failing': score.failing,
}
summary['control_summary'] = {
k: {'title': v['title'], 'overall_status': v['overall_status'], 'pass_count': v['pass_count'], 'fail_count': v['fail_count']}
for k, v in get_control_summary(scan.findings).items()
if v['has_automated_checks'] or v['overall_status'] != 'not_assessed'
}
print(json.dumps(summary, indent=2))
"
```

**For both AWS + Azure:**
```bash
<PYTHON_CMD> -c "
import json
from shasta.config import get_aws_client, get_azure_client
from shasta.scanner import run_full_scan
from shasta.compliance.mapper import get_control_summary
from shasta.compliance.scorer import calculate_score
from shasta.reports.summary import summarize_scan
from shasta.reports.generator import save_markdown_report, save_html_report
from shasta.db.schema import ShastaDB

client = get_aws_client()
client.validate_credentials()
azure_client = get_azure_client()
azure_client.validate_credentials()
print('Running full compliance scan (AWS + Azure)...')
scan = run_full_scan(client, azure_client=azure_client)
cfg = load_config()
clients = {}
if cfg.get('aws_profile'):
c = get_aws_client(); c.validate_credentials(); clients['client'] = c
if cfg.get('azure_subscription_id'):
a = get_azure_client(); a.validate_credentials(); clients['azure_client'] = a
if cfg.get('gcp_project_id'):
g = get_gcp_client(); g.validate_credentials(); clients['gcp_client'] = g
if not clients:
raise SystemExit('No clouds configured. Run /connect-aws, /connect-azure, or /connect-gcp first.')

labels = {'client': 'AWS', 'azure_client': 'Azure', 'gcp_client': 'GCP'}
print(f\"Running full compliance scan ({' + '.join(labels[k] for k in clients)})...\")
scan = run_full_scan(**clients)
db = ShastaDB(); db.initialize(); db.save_scan(scan)

md = save_markdown_report(scan)
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ Thumbs.db
.env
.env.local

# Local CLI binaries (uv, uvx, etc.) — installed per-developer
.bin/

# User-specific config (template is shasta.config.template.json)
shasta.config.json

Expand Down
23 changes: 14 additions & 9 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,29 @@
# Shasta — Multi-Cloud Compliance Automation

## What is this?
Shasta is a Claude Code-native multi-cloud compliance platform. It scans AWS and Azure environments, maps findings to compliance controls (SOC 2, ISO 27001, HIPAA, ISO 42001, EU AI Act), generates remediation guidance (with Terraform), produces compliance policies and reports, and ships an optional voice-driven dashboard (`python -m shasta.voice`) for hands-free posture queries.
Shasta is a Claude Code-native multi-cloud compliance platform. It scans AWS, Azure, and GCP environments, maps findings to compliance controls (SOC 2, ISO 27001, HIPAA, ISO 42001, EU AI Act), generates remediation guidance (with Terraform), produces compliance policies and reports, and ships an optional voice-driven dashboard (`python -m shasta.voice`) for hands-free posture queries.

## Tech stack
- Python 3.11+, boto3, azure-identity, azure-mgmt-*, msgraph-sdk, rich, pydantic, jinja2, weasyprint
- Python 3.11+, boto3, azure-identity, azure-mgmt-*, msgraph-sdk, google-auth, google-api-python-client, google-cloud-storage, rich, pydantic, jinja2, weasyprint
- SQLite for local data storage
- Claude Code skills for user interface

## Project layout
- `src/shasta/` — cloud compliance library (SOC 2, ISO 27001)
- `src/shasta/aws/` — AWS check modules (boto3)
- `src/shasta/azure/` — Azure check modules (azure-mgmt-*, msgraph-sdk)
- `src/shasta/gcp/` — GCP check modules (google-api-python-client, google-cloud-storage)
- `src/shasta/compliance/ai/` — AI governance frameworks (ISO 42001, EU AI Act, NIST AI RMF)
- `src/shasta/aws/ai_checks.py` — AWS AI service checks (Bedrock, SageMaker)
- `src/shasta/azure/ai_checks.py` — Azure AI service checks (Azure OpenAI, Azure ML)
- `src/shasta/dashboard/` — read-only HTML compliance dashboard (FastAPI + Jinja, port 8080)
- `src/shasta/voice/` — opt-in voice console (FastAPI + React + OpenAI Realtime, port 8090); install with `pip install -e ".[voice]"`
- `.claude/skills/` — Claude Code skill definitions
- `tests/` — pytest test suite (uses moto for AWS mocking, unittest.mock for Azure); voice-specific tests live under `tests/voice/`
- `tests/` — pytest test suite (uses moto for AWS mocking, unittest.mock for Azure and GCP); voice-specific tests live under `tests/voice/`, GCP tests under `tests/test_gcp/`
- `data/` — runtime data (gitignored)

## Commands
- Install: `pip install -e ".[dev]"` (core), `pip install -e ".[dev,azure]"` (with Azure), or `pip install -e ".[dev,azure,voice]"` (with voice console)
- Install: `pip install -e ".[dev]"` (core), `pip install -e ".[dev,azure]"` (with Azure), `pip install -e ".[dev,gcp]"` (with GCP), or `pip install -e ".[dev,azure,gcp,voice]"` (everything)
- Test: `pytest` (or `pytest tests/voice/` for voice-only)
- Lint: `ruff check src/ tests/`
- Format: `ruff format src/ tests/`
Expand All @@ -33,6 +34,7 @@ Shasta is a Claude Code-native multi-cloud compliance platform. It scans AWS and
- Use pydantic models for all data structures
- All AWS calls go through `src/shasta/aws/client.py` session management
- All Azure calls go through `src/shasta/azure/client.py` session management
- All GCP calls go through `src/shasta/gcp/client.py` session management
- Every check function returns a list of `Finding` objects
- Use `rich` for terminal output formatting
- Keep functions focused — one check per function
Expand All @@ -58,11 +60,13 @@ they are in context at every conversation start:
`tests/test_whitney/test_integrity.py` enforce this for Whitney; extend
the same pattern when adding new modules.

3. **Default to multi-region / multi-subscription.** New AWS check modules
must iterate `client.get_enabled_regions()` via `client.for_region(r)`.
New Azure check modules must call sites via `AzureClient.for_subscription(sid)`
when scanning multiple subscriptions. Single-region/single-subscription is
the explicit override, never the default.
3. **Default to multi-region / multi-subscription / multi-project.** New AWS
check modules must iterate `client.get_enabled_regions()` via
`client.for_region(r)`. New Azure check modules must call sites via
`AzureClient.for_subscription(sid)` when scanning multiple subscriptions.
New GCP check modules must iterate projects via `GCPClient.for_project(pid)`.
Single-region / single-subscription / single-project is the explicit
override, never the default.

4. **Frameworks belong on the `Finding` model.** Populate
`soc2_controls`, `cis_aws_controls`, `cis_azure_controls`, `mcsb_controls`,
Expand Down Expand Up @@ -104,6 +108,7 @@ they are in context at every conversation start:
- `pytest tests/test_integrity/` — doc-vs-code drift integrity tests
- `pytest tests/test_aws/test_aws_sweep_smoke.py` — AWS module structural smoke tests
- `pytest tests/test_azure/test_smoke.py` — Azure module structural smoke tests
- `pytest tests/test_gcp/test_smoke.py` — GCP module structural smoke tests

These are mechanically enforced by `.github/workflows/integrity.yml` on every PR.
A drift in any tracked numeric claim (or a stub-shaped function, or a missing
Expand Down
Loading
Loading