# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Overview

This is a Terraform static code analysis tool — a wrapper around [Checkov](https://www.checkov.io/) that scans IaC repositories for security issues, then posts results as GitHub PR comments and commit status checks. It runs as a Docker container triggered from CI/CD pipelines or manually.

## Commands

### Running Unit Tests

Unit tests validate custom policies against Terraform fixture files in `tests/`. They require Docker:

```bash
docker compose run --rm unit-test
```

Or directly (requires `checkov` and `jq` installed locally):

```bash
./run-tests.sh
```

The test runner expects exact counts: **68 failed, 71 passed, 86 skipped, 0 parsing errors, 57 resources**. If you add/modify policies or test fixtures, update the expected counts in `run-tests.sh`.

### Running Integration Tests

Requires a `.env` file (copy from `.env.shadow`) with GitHub credentials and a target PR:

```bash
cp .env.shadow .env
# Fill in GITHUB_API_KEY, GITHUB_REPO_NAME, GITHUB_PR_NUM

docker compose run --rm integration-test
# Or as GitHub App:
docker compose run --rm integration-test-as-github-app
```

### Module Integration Tests

Downloads 27 official Terraform modules and scans them (requires GitHub App credentials):

```bash
docker compose run --rm module-integration-tests
```

### Building the Docker Image

```bash
docker build --target base -t terraform-static-code-analysis .
docker build --target unit-test -t terraform-static-code-analysis:unit-test .
```

## Architecture

### Application Flow (`terraform-scanner/`)

1. **`main.py`** — Entry point. Reads environment variables, authenticates with GitHub, fetches PR metadata, invokes the scanner, then publishes results.
2. **`scanner.py`** — Shells out to `checkov` CLI, captures JSON output, parses pass/fail/skip counts and individual check results.
3. **`pull_request.py`** — GitHub API wrapper using PyGithub. Supports two auth modes: personal access token (`GITHUB_API_KEY`) and GitHub App (client ID + installation ID + private key). Clones the target repo, posts comments, sets commit statuses.
4. **`filter.py`** — Filters and prioritizes check results, handles recursive scan logic.
5. **`output.py`** — Abstract output classes: `ConsoleOutput` (stdout) and `GitHubOutput` (PR comments/status).
6. **`constants.py`** — Shared constants, notably the GitHub notification prefix used to identify and replace bot comments.

### Custom Policies (`policies/`)

All custom policies use the `ORCD_*` check ID prefix. They extend Checkov's `BaseResourceCheck` or `BaseResourcePair`.

- **`policies/simple/`** — Reserved for simple declarative checks (currently empty).
- **`policies/advanced/`** — Python-based checks with optional AWS/Snowflake API calls:
  - `ORCD_AWS_1` (`SubnetsWithIPShortage.py`) — Calls AWS EC2 API to check available IPs in subnets.
  - `ORCD_AWS_2` (`NonCompliantModules.py`) — Validates module versions are pinned to approved semver tags.
  - `ORCD_AWS_3–9` (various `CloudFront*.py`) — Validates CloudFront response headers configuration.
  - `ORCD_AWS_10–12` — IAM and tagging checks.
  - `ORCD_SNOWFLAKE_1` (`SnowflakeResourcePlacement.py`) — Validates Snowflake resource placement.
  - `tools.py` — Shared utilities for the advanced policies.

### Checkov Configuration (`checkov.yaml`)

Defines framework (Terraform), points to custom policy directories, enables variable evaluation and external module download. Contains 70+ explicitly disabled upstream checks with documented reasons — preserve these when making changes.

### Environment Variables

| Variable | Purpose |
|---|---|
| `GITHUB_API_KEY` | Personal access token (alternative to App auth) |
| `GITHUB_APP_CLIENT_ID` | GitHub App client ID |
| `GITHUB_APP_INSTALLATION_ID` | GitHub App installation ID |
| `GITHUB_APP_PRIVATE_KEY` | GitHub App private key |
| `GITHUB_REPO_NAME` | Target repo in `<org>/<repo>` format |
| `GITHUB_PR_NUM` | PR number to analyze |
| `RECURSIVE_SCAN` | Scan nested modules (default: `true`) |
| `BLOCKING_MODE` | Post blocking review instead of comment (default: `false`) |
| `AWS_ROLES_PER_PREFIX` | JSON map of AWS role ARNs for dynamic checks |

### Adding a New Custom Policy

1. Create a new file in `policies/advanced/` (or `policies/simple/`).
2. Assign the next available `ORCD_*` ID.
3. Add corresponding Terraform fixture files under `tests/`.
4. Update expected counts in `run-tests.sh` (failed/passed/skipped/resources).
5. For AWS API-based checks: handle exceptions by logging to stderr and returning `CheckResult.SKIPPED` (never fail on API errors).
6. Update `README.md` with the new policy entry and its Notion doc link.

### Dynamic (AWS API) Checks

When `AWS_ROLES_PER_PREFIX` is set, policies like `SubnetsWithIPShortage` assume an AWS role to make live API calls. The variable is a JSON object mapping resource name prefixes to role ARNs. Missing API access must result in `SKIPPED`, not `FAILED`.
