--- name: common-security description: >- Security guardrails for Envilder (CLI, GitHub Action, SDKs, CDK, website). Covers secret handling, credential hygiene, supply chain safety, input validation, and CI/CD security. Use when reviewing code for security, handling secrets, validating CLI input, or reviewing GitHub Actions workflows. user-invocable: false --- # Security Skill Security guardrails adapted to the Envilder project: a CLI + GitHub Action + multi-runtime SDK platform that manages secrets from AWS SSM and Azure Key Vault. ## When to Use - Reviewing code that handles secrets or cloud credentials - Adding new CLI options or GHA inputs that accept external data - Reviewing GitHub Actions workflows for credential safety - Adding new infrastructure (CDK stacks) - Reviewing SDK code that interacts with cloud provider APIs - Modifying the website (Astro) with user-visible content ## 1. Secret Handling ### Never Expose Secrets in Output - **CLI/GHA**: Use `EnvironmentVariable.maskedValue` (shows last 3 chars) for logging - **SDKs**: Never log resolved secret values: log only the key name - **Tests**: Use `envilder.json` to resolve test tokens; never hardcode tokens - **Website**: No secrets: it's a static site ### Storage Rules | Context | Where secrets live | Never | | ------- | ------------------ | ----- | | Production | AWS SSM Parameter Store (encrypted) | In code, env vars, or config files | | Production (Azure) | Azure Key Vault | In code or checked-in files | | CI | GitHub Secrets → OIDC → SSM | As plaintext in workflow YAML | | Local dev | AWS profile → SSM (via `envilder.json`) | In `.env` files committed to Git | | Tests | TestContainers (LocalStack/Lowkey Vault) | Real credentials in test code | ### Secretlint Enforcement Secretlint runs on every `pnpm lint` invocation and scans **all files** for credential patterns (AWS keys, tokens, private keys). If Secretlint fails, the commit is blocked. ## 2. Credential Hygiene in CI/CD ### GitHub Actions OIDC - **Always** use `aws-actions/configure-aws-credentials` with `role-to-assume` - **Never** store `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` as GitHub Secrets - OIDC tokens are short-lived and scoped: no rotation needed ### GitHub Action Inputs - GHA reads inputs from `process.env.INPUT_*`: validate before use - The `map` input (file path) must be validated to prevent path traversal - Never interpolate GHA inputs directly into shell commands ### Workflow Permissions - Use minimal `permissions:` block in every workflow - `contents: read` for checkout, `id-token: write` for OIDC - Never use `permissions: write-all` ## 3. Input Validation ### CLI - Commander validates option types: but validate semantic constraints: - `--map` path must exist and be a `.json` file - `--provider` must be one of `aws` | `azure` (case-insensitive) - `--vault-url` must be a valid HTTPS URL matching `*.vault.azure.net` - Never pass CLI arguments to shell commands unsanitized - Use custom domain errors (`InvalidArgumentError`): not generic exceptions ### SDKs - Map file paths: validate existence before parsing - JSON parsing: handle malformed JSON gracefully (domain error, not stack trace) - Provider names: strict enum matching, reject unknown values - Cross-provider validation: profile + Azure → `InvalidArgumentError` ### Website - Static site (Astro): no user input at runtime - Build-time i18n: translation keys are developer-controlled, not user-supplied ## 4. Supply Chain Security ### Dependency Pinning | Stack | Mechanism | File | | ----- | --------- | ---- | | TypeScript | `pnpm-lock.yaml` + `catalog:` versions | `pnpm-workspace.yaml` | | .NET | Central Package Management | `Directory.Packages.props` | | Python | `uv.lock` (deterministic) | `uv.lock` | ### Rules - Lock files **must** be committed: never `.gitignore` them - Dependabot (or Renovate) configured for automatic dependency updates - Review advisories on every dependency update PR - esbuild bundles GHA: verify the bundle is up to date (`pnpm verify:gha`) - Pin GitHub Actions to full commit SHA (not `@v4` tags) in production workflows ### CDK - Keep `aws-cdk-lib` up to date: security patches affect deployed infra - CDK synth output (`cdk.out/`) is `.gitignored`: never commit CloudFormation templates ## 5. SDK-Specific Security ### AWS SSM Provider - Always use `WithDecryption: true` for SecureString parameters - Never log the decrypted parameter value - Credential chain: SDK default chain (env vars → profile → instance role) - If `profile` is specified, only use `CredentialProfileStoreChain`: don't mix ### Azure Key Vault Provider - Use `DefaultAzureCredential`: never hardcode `clientId`/`clientSecret` - Vault URL validation: must match `https://*.vault.azure.net` - TLS certificate validation: enabled in production, only disabled in tests against Lowkey Vault (emulator) ### Cross-Provider - `EnvilderOptions` overrides `$config`: validate that overrides don't introduce insecure combinations (e.g., disabling encryption) - Missing secrets → `null`/`None` (silent). Validation is opt-in via `validateSecrets()`: document this to users clearly ## 6. Website Security - Astro generates static HTML: no server-side injection possible - External links: use `rel="noopener noreferrer"` on `target="_blank"` links - No inline scripts or `dangerouslySetInnerHTML` equivalents - CSP headers configured at CDN/CloudFront level (via CDK) ## 7. Testing Security - Acceptance tests use emulators (LocalStack, Lowkey Vault): never real cloud endpoints - `LOCALSTACK_AUTH_TOKEN` resolved via Envilder itself (dogfooding): stored in SSM, never in code - Test cleanup: containers destroyed after test run (TestContainers lifecycle) - TLS disabled only for Lowkey Vault container tests (self-signed cert) ## Quick Security Checklist Before merging any PR, verify: - [ ] No secrets hardcoded in code, config, or test files - [ ] Secretlint passes (`pnpm lint`) - [ ] CLI/SDK input validated with domain errors (not generic exceptions) - [ ] Cloud credentials use OIDC (CI) or SDK default chain (runtime) - [ ] Secrets logged only via `maskedValue` (last 3 chars visible) - [ ] Lock files updated if dependencies changed - [ ] GHA workflows use minimal `permissions:` - [ ] No `shell=True` or unsanitized argument interpolation in scripts