From 638651add58160ec8a41f106d926567cdb2e7253 Mon Sep 17 00:00:00 2001 From: KK Mookhey Date: Fri, 15 May 2026 07:31:31 -0700 Subject: [PATCH 1/4] feat(gcp): wire GCP into config and connect/scan skills MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The GCP check modules and scanner integration already existed, but the config layer and skill-driven UX did not — there was no way to drive a GCP scan through the normal flow. - add gcp_project_id / gcp_region to ShastaConfig (with project-id format validator) and the load_config defaults - add get_gcp_client() convenience constructor, mirroring get_aws_client() / get_azure_client() - add /connect-gcp skill mirroring /connect-azure (ADC auth via gcloud, [gcp] extra install, credential validation) - collapse the three near-duplicate per-cloud blocks in /scan into one parameterized block that scans whichever clients are configured Co-Authored-By: Claude Opus 4.7 (1M context) --- .claude/skills/connect-gcp/SKILL.md | 61 +++++++++++++++ .claude/skills/scan/SKILL.md | 115 ++++++---------------------- src/shasta/config.py | 26 +++++++ 3 files changed, 111 insertions(+), 91 deletions(-) create mode 100644 .claude/skills/connect-gcp/SKILL.md diff --git a/.claude/skills/connect-gcp/SKILL.md b/.claude/skills/connect-gcp/SKILL.md new file mode 100644 index 0000000..e64add1 --- /dev/null +++ b/.claude/skills/connect-gcp/SKILL.md @@ -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 + -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 + -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 + -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 `` 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 `. +- 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. diff --git a/.claude/skills/scan/SKILL.md b/.claude/skills/scan/SKILL.md index 9cdf4a3..43c290e 100644 --- a/.claude/skills/scan/SKILL.md +++ b/.claude/skills/scan/SKILL.md @@ -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 --- @@ -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 ``). +Read `shasta.config.json` for `python_cmd`, `aws_profile`, `azure_subscription_id`, and `gcp_project_id`. Use that for all commands (shown as ``). **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 @@ -39,13 +40,15 @@ 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 -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 @@ -53,90 +56,20 @@ 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 - -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 - -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) diff --git a/src/shasta/config.py b/src/shasta/config.py index 3669913..62c2e5a 100644 --- a/src/shasta/config.py +++ b/src/shasta/config.py @@ -25,6 +25,8 @@ _UUID_PATTERN = re.compile( r"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$", re.IGNORECASE ) +# GCP project ID: 6-30 chars, lowercase letter start, letters/digits/hyphens, no trailing hyphen +_GCP_PROJECT_ID_PATTERN = re.compile(r"^[a-z][a-z0-9-]{4,28}[a-z0-9]$") class ShastaConfig(BaseModel): @@ -35,6 +37,8 @@ class ShastaConfig(BaseModel): azure_subscription_id: str = "" azure_tenant_id: str = "" azure_region: str = "" + gcp_project_id: str = "" + gcp_region: str = "" python_cmd: str = "" company_name: str = "" github_repos: list[str] = [] @@ -63,6 +67,16 @@ def validate_tenant_id(cls, v: str) -> str: ) return v + @field_validator("gcp_project_id") + @classmethod + def validate_gcp_project_id(cls, v: str) -> str: + if v and not _GCP_PROJECT_ID_PATTERN.match(v): + raise ValueError( + f"gcp_project_id must be a valid GCP project ID (got '{v}'). " + "Run 'gcloud config get-value project' to find your project ID." + ) + return v + @field_validator("jira_base_url") @classmethod def validate_jira_url(cls, v: str) -> str: @@ -117,6 +131,8 @@ def load_config() -> dict[str, Any]: "azure_subscription_id": "", "azure_tenant_id": "", "azure_region": "", + "gcp_project_id": "", + "gcp_region": "", "python_cmd": _detect_python_cmd(), "company_name": "", "github_repos": [], @@ -199,3 +215,13 @@ def get_azure_client(): tenant_id=tenant_id, region=region, ) + + +def get_gcp_client(): + """Convenience: create a GCPClient from config.""" + from shasta.gcp.client import GCPClient + + cfg = load_config() + project_id = cfg.get("gcp_project_id") or None + region = cfg.get("gcp_region") or None + return GCPClient(project_id=project_id, region=region) From a3a0523ae4e3aac8a406833a94be97fee3f2c5ba Mon Sep 17 00:00:00 2001 From: KK Mookhey Date: Fri, 15 May 2026 07:31:31 -0700 Subject: [PATCH 2/4] docs: refresh TRUST.md test counts and suite breakdown after GCP merge The GCP integration (PR #20) added ~306 tests but left TRUST.md's internal test counts stale and self-contradictory (TL;DR said 814, Layer 1 said 519/684, run-output said 537). Sync every count to the real collected total of 851, add tests/test_gcp and tests/voice rows to the Layer 1 breakdown, and correct the integrity-test list (it named three Whitney tests deleted in the 2026-04-13 split). Co-Authored-By: Claude Opus 4.7 (1M context) --- TRUST.md | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/TRUST.md b/TRUST.md index 829a1c2..e4059a7 100644 --- a/TRUST.md +++ b/TRUST.md @@ -23,7 +23,7 @@ Shasta and Whitney together ship the following, all integrity-tested: - **267 check functions** (267 cloud compliance + 0 AI governance — Whitney now ships as a separate repo at [github.com/transilienceai/whitney](https://github.com/transilienceai/whitney); install with `pip install whitney` for source-code scanning) - **132 Terraform remediation templates** (81 AWS + 31 Azure + 20 GCP) -- **814 tests** that all pass on every commit +- **851 tests** that all pass on every commit None of the claims in this README are written by hand and hoped-for — every numeric claim is AST-counted from source by an integrity test @@ -38,7 +38,7 @@ results six months from now on the same infrastructure. ``` Layer 0 — Deterministic detection (no LLM in the pipeline) -Layer 1 — Test suite (684 tests, all green on every commit) +Layer 1 — Test suite (851 tests, all green on every commit) Layer 2 — Doc-vs-code drift integrity tests Layer 3 — Multi-region structural enforcement Layer 4 — CI workflow (mechanical PR-time enforcement) @@ -98,24 +98,26 @@ py -3.12 -m pytest tests/test_whitney/test_integrity.py -v ### Layer 1 — Test suite -**519 tests** across `tests/`, all green on every commit. Breakdown: +**851 tests** across `tests/`, all green on every commit. Breakdown: | Suite | Tests | What it covers | |---|---|---| | `tests/test_whitney/` | 205 | AI compliance frameworks (ISO 42001, EU AI Act, OWASP LLM Top 10, OWASP Agentic, NIST AI RMF, NIST AI 600-1, MITRE ATLAS), AI policies, AI SBOM (code + AWS + Azure), mapper + scorer, integrity. The Whitney corpus eval lives in the standalone Whitney repo. | -| `tests/test_aws/` | 108 | AWS smoke tests: imports, runner signatures, multi-region structural enforcement, Terraform template renders, deprecated runtime tables, VPC endpoint expectations | -| `tests/test_azure/` | 58 | Azure smoke tests: imports, runner signatures, diagnostic settings matrix, Defender required plans, CIS 5.2.x activity log alert mappings, Terraform template renders, Entra ID checks | -| `tests/test_compliance/` | 21 | SOC 2 + ISO 27001 scorer + mapper | -| `tests/test_workflows/` | 26 | Risk register + drift detection workflows | -| `tests/test_integrity/` | 11 | **Doc-vs-code drift** — see Layer 2 | -| `tests/test_reports/` | 13 | Markdown / HTML / PDF report generation | -| Other | ~80 | Conftest, models, client, AWS/Azure unit fixtures | +| `tests/test_gcp/` | 142 | GCP smoke + functional tests: imports, runner signatures, multi-project iteration, `NOT_ASSESSED`-on-error behavior, Terraform template renders, CIS GCP control mappings | +| `tests/test_aws/` | 140 | AWS smoke tests: imports, runner signatures, multi-region structural enforcement, Terraform template renders, deprecated runtime tables, VPC endpoint expectations | +| `tests/test_azure/` | 97 | Azure smoke tests: imports, runner signatures, diagnostic settings matrix, Defender required plans, CIS 5.2.x activity log alert mappings, Terraform template renders, Entra ID checks | +| `tests/voice/` | 74 | Voice console: tool endpoints, score / finding / risk queries, ephemeral token minting | +| `tests/test_compliance/` | 71 | SOC 2 + ISO 27001 scorer + mapper | +| `tests/test_reports/` | 45 | Markdown / HTML / PDF report generation | +| `tests/test_workflows/` | 44 | Risk register + drift detection workflows | +| `tests/test_integrity/` | 15 | **Doc-vs-code drift** — see Layer 2 | +| Other | ~18 | Conftest, models, client, integration + trust-center fixtures | **Run them yourself:** ```bash py -3.12 -m pytest --ignore=tests/test_rainier -# 537 passed in ~2 minutes +# 851 passed in ~2 minutes ``` The full suite runs on every PR via @@ -129,19 +131,23 @@ The full suite runs on every PR via This is the layer that closes the most embarrassing failure mode: **numeric claims in the README that don't match the code.** -`tests/test_integrity/test_doc_claims.py` contains **16 parametrized assertions** that AST-count the source tree and assert each numeric claim in `README.md` and `TRUST.md` matches reality: +`tests/test_integrity/test_doc_claims.py` contains **15 parametrized assertions** that AST-count the source tree and assert each numeric claim in `README.md` and `TRUST.md` matches reality: | Test | Claim it verifies | Source of truth | |---|---|---| | `test_readme_total_check_count` | "5 Domains, X+ Checks" headline | AST count of `check_*` in `src/shasta/` | | `test_readme_technical_controls_check_count` | Technical-controls table row | AST count of `check_*` in `src/shasta/` | +| `test_readme_intro_total_check_count` | Intro-paragraph "N automated checks" | AST count of `check_*` in `src/shasta/` | +| `test_readme_intro_terraform_template_count` | Intro-paragraph "N Terraform templates" | `TERRAFORM_TEMPLATES` registry | | `test_readme_check_to_risk_mapping_count` | "X check-to-risk mappings" | `len(FINDING_TO_RISK)` | | `test_readme_test_count_in_tree_block` | "(N+ tests)" annotation | `pytest --collect-only` | -| `test_readme_ai_check_count` | "N AI checks (code + cloud)" | AST count of `check_*` in `src/whitney/` | -| `test_readme_terraform_template_breakdown` | "X templates (Y AWS + Z Azure)" | `TERRAFORM_TEMPLATES` registry | +| `test_readme_terraform_template_breakdown` | "X templates (Y AWS + Z Azure + W GCP)" | `TERRAFORM_TEMPLATES` registry | | `test_readme_policy_template_count` | "N SOC 2 policy documents" | `len(POLICIES)` | -| `test_whitney_readme_test_count` | Whitney status checklist test count | `pytest tests/test_whitney/ --collect-only` | -| `test_whitney_trust_unit_test_count` | TRUST.md Layer 1 header | `pytest tests/test_whitney/ --collect-only` | +| `test_root_trust_total_check_count` | TRUST.md TL;DR "N check functions" | AST count of `check_*` in `src/shasta/` | +| `test_root_trust_terraform_template_count` | TRUST.md TL;DR Terraform template count | `TERRAFORM_TEMPLATES` registry | +| `test_root_trust_test_count` | TRUST.md TL;DR "N tests" bullet | `pytest --collect-only` | +| `test_root_trust_layer1_test_breakdown` | TRUST.md Layer 1 table Whitney row | `pytest tests/test_whitney/ --collect-only` | +| `test_root_trust_integrity_test_count` | TRUST.md "N parametrized assertions" | `pytest tests/test_integrity/ --collect-only` | | `test_counters_return_positive_numbers` | self-test of helper functions | n/a | | `test_pytest_collector_works` | self-test of pytest collector | n/a | @@ -179,7 +185,7 @@ introduced minutes earlier. ```bash py -3.12 -m pytest tests/test_integrity/ -v -# 11 passed +# 15 passed ``` --- From 8f5b1fb85eda545ec604f2d5c61c33da3a929f90 Mon Sep 17 00:00:00 2001 From: KK Mookhey Date: Fri, 15 May 2026 07:31:31 -0700 Subject: [PATCH 3/4] docs: add GCP to CLAUDE.md project map and conventions PR #20 added src/shasta/gcp/ but left CLAUDE.md describing an AWS+Azure-only platform. Add GCP to the project description, tech stack, project layout, install extras, client-session convention, the multi-region/subscription principle (now multi-project via GCPClient.for_project), and the required smoke-test list. Co-Authored-By: Claude Opus 4.7 (1M context) --- CLAUDE.md | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index b0c5348..dbc5400 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,10 +1,10 @@ # 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 @@ -12,17 +12,18 @@ Shasta is a Claude Code-native multi-cloud compliance platform. It scans AWS and - `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/` @@ -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 @@ -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`, @@ -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 From 0cf6bc416bbd30841eb8ff40d0dd4c2f93533edd Mon Sep 17 00:00:00 2001 From: KK Mookhey Date: Fri, 15 May 2026 07:32:06 -0700 Subject: [PATCH 4/4] chore: gitignore .bin/ local CLI binaries Each developer can bootstrap uv/uvx into ./.bin/ without committing the 48MB binary blobs. Co-Authored-By: Claude Opus 4.7 (1M context) --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 90c9060..fff32f6 100644 --- a/.gitignore +++ b/.gitignore @@ -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