feat(nix-review): add Nix-focused infrastructure review skill

Multi-lens review for NixOS modules, flakes, and packages:
- module-safety, secrets, systemd-hardening
- determinism, closure-hygiene, flake-hygiene
- supply-chain lenses
This commit is contained in:
dan 2026-01-24 09:40:22 -08:00
parent 5759ccc8fe
commit b11b3b55ae
13 changed files with 568 additions and 0 deletions

View file

@ -0,0 +1,59 @@
# Nix Review Lenses
This document describes the recommended format for Nix review lenses stored in `~/.config/lenses/nix/`.
## Lens File Layout
Each lens is a Markdown file named after the lens (e.g., `determinism.md`). Keep lenses focused, actionable, and low-noise.
**Recommended sections:**
1. **Purpose**
- What the lens checks and why it matters.
2. **Checks**
- Bullet list of specific patterns or rules.
- Keep checks concrete (e.g., “`import <nixpkgs>` in flake-based repos”).
3. **Severity Guidance**
- Map checks to default severities (HIGH/MED/LOW).
4. **Examples**
- Brief “bad” and “good” snippets.
## Finding Format
Findings should use the standard format:
```
[TAG] <severity:HIGH|MED|LOW> <file:line>
Issue: <whats wrong>
Suggest: <how to fix>
Evidence: <why it matters>
```
## Lens Tags
Use a stable, uppercase tag per lens (e.g., `DETERMINISM`, `SECRETS`, `MODULE-SAFETY`).
## Suggested Lens List
Phase 1:
- determinism
- secrets
- closure-hygiene
Phase 2:
- flake-hygiene
- module-safety
- systemd-hardening
Phase 3:
- derivation-diff
- closure-impact
- supply-chain
## Tips
- Prefer explicit, actionable checks over style opinions.
- Avoid duplicate checks across lenses; rely on root-cause grouping instead.
- Keep severity consistent to reduce alert fatigue.

120
skills/nix-review/README.md Normal file
View file

@ -0,0 +1,120 @@
# Nix Review Skill
Nix-focused infrastructure review for flakes, NixOS/Home Manager modules, overlays, and packaging. Linter-first (statix/deadnix/format) plus optional deep lenses for derivation diffs, closure impact, and supply-chain scanning.
## Quick Start
**Claude Code / OpenCode:**
The agent will invoke this skill when you ask to review Nix configs.
**Manual invocation:**
```
/nix-review
/nix-review path/to/module.nix
/nix-review --eval
/nix-review --quick
/nix-review --deep
```
## What It Does
- Runs Nix linting and unused-binding checks (statix/deadnix)
- Runs formatter check only when the repo declares one (treefmt, flake formatter, or `nix fmt`)
- Runs `nix flake check` when `flake.nix`/`flake.lock` changed or with `--eval` (may build; skip with `--quick`)
- Runs secrets pre-check before any eval; skips eval if a potential secret is detected
- If NixOS modules changed, prompts for `nixosConfigurations` to evaluate (or uses `NIX_REVIEW_TARGETS`)
- Applies Nix-specific lenses (determinism, secrets, module safety, strict flake hygiene)
- Optionally runs deep analysis (nix-diff/nvd, closure impact, sbomnix+grype)
- Summarizes findings (grouped by root cause) and asks which to file as issues (default: file all non-lint)
## Prerequisites
- `nix` CLI
- `statix`
- `deadnix`
- Nix formatter installed if the repo declares one
- Optional deep tools: `nix-diff`, `nvd`, `sbomnix`, `grype`, `nix-tree`
## Example Usage
### Scenario: Default review with eval
**User request:**
```
/nix-review
```
**Agent actions:**
1. Collects changed Nix files
2. Runs statix/deadnix/formatter checks
3. Runs `nix flake check` if `flake.nix`/`flake.lock` changed or `--eval` is set
4. If NixOS modules changed, prompts for `nixosConfigurations` (or uses `NIX_REVIEW_TARGETS`)
5. Applies Phase 12 lenses
6. Summarizes findings and asks what to file
### Scenario: Quick lint-only review
**User request:**
```
/nix-review --quick
```
**Agent actions:**
1. Collects changed Nix files
2. Runs statix/deadnix/formatter checks
3. Skips `nix flake check`
4. Applies Phase 12 lenses
5. Summarizes findings and asks what to file
### Scenario: Deep supply-chain + impact analysis
**User request:**
```
/nix-review --deep
```
**Agent actions:**
1. Runs default lint checks
2. Adds derivation diff + closure impact
3. Generates SBOM and scans with grype
4. Summarizes findings (all severities) and asks what to file
**Note:** Closure impact runs when `flake.lock` changes; it will prompt before running unless `--deep` is set.
## Configuration
No configuration required by default. Lenses are loaded from:
```
~/.config/lenses/nix/
```
Starter lens templates are available in:
```
skills/nix-review/lenses/
```
### Optional repo config: `.nix-review.toml`
```toml
formatter = "alejandra" # or "nixfmt-rfc-style"
eval = "auto" # auto|always|never
closure_impact = "auto" # auto|always|never
issue_filing = "non-lint" # all|non-lint|high-only
nixos_targets = ["nixosConfigurations.laptop", "nixosConfigurations.server"]
```
Notes:
- `eval = auto` runs `nix flake check` when `flake.nix` or `flake.lock` changes.
- `closure_impact = auto` runs when `flake.lock` changes (prompts unless `--deep`).
- `nixos_targets` overrides `NIX_REVIEW_TARGETS` and avoids prompts.
- Precedence: CLI flags > `.nix-review.toml` > environment variables.
Optional: set `NIX_REVIEW_TARGETS` to a comma-separated list of `nixosConfigurations` to evaluate (avoids prompts).
## Troubleshooting
### Problem: Missing tool errors
**Cause:** Tool not installed or not in PATH.
**Solution:** The review skips missing tools and reports a warning. Install via nix or system package manager for full coverage.
### Problem: Deep scan is slow
**Cause:** sbomnix/grype or closure diff can be expensive.
**Solution:** Run without `--deep` or narrow the target path.
## See Also
- [ops-review](../ops-review/README.md)
- [code-review](../code-review/README.md)

148
skills/nix-review/SKILL.md Normal file
View file

@ -0,0 +1,148 @@
---
name: nix-review
description: Nix-focused infrastructure review. Linter-first workflow for flakes, NixOS modules, packages, and Nix CI with eval-aware lenses and actionable fixes.
---
# Nix Review Skill
Run a Nix-specific review across flakes, NixOS/Home Manager modules, overlays, and packaging. Uses a linter-first approach, then applies Nix-centric lenses with optional evaluation checks. Findings are summarized and presented for approval before filing issues.
## When to Use
Invoke this skill when the user requests:
- "Review my Nix configs"
- "Check my flake for issues"
- "Audit NixOS modules"
- "Run nix review"
- `/nix-review`
## Arguments
- `/nix-review` - Runs Phase 12 lenses; runs `nix flake check` if `flake.nix` or `flake.lock` changed
- `/nix-review path/` - Targets a file or directory
- `/nix-review --eval` - Forces `nix flake check`
- `/nix-review --quick` - Skips eval steps (even if `flake.lock` changed)
- `/nix-review --deep` - Includes Phase 3 lenses (derivation diff, closure impact, supply-chain scan)
## Scope
**Primary targets**
- `*.nix`, `flake.nix`, `flake.lock`
- NixOS/Home Manager modules
- Nix packaging (pkgs/, overlays)
- Flake check/eval workflows
- Secrets management (`sops-nix`, `agenix`)
**Default target**
- Git diff of uncommitted changes, unless a file/dir is specified
## Process
1. **Target Selection**
- Parse optional target; default to git diff
- Confirm scope if >10 files
2. **Static Tools (Linter-First)**
- `statix check` (lint)
- `deadnix --fail` (unused bindings)
- Formatter check only when repo config exists (treefmt, `nix fmt`, or flake formatter)
3. **Secrets Pre-Check**
- Run `secrets` lens before any eval
- If a potential secret is detected, warn and skip eval steps
4. **Eval Checks (Default)**
- Run `nix flake check` when `flake.nix` or `flake.lock` changed, or when `--eval` is set (may build; use `--quick` to skip)
- Skip with `--quick`
- Optional: `nix flake metadata` / `nix flake show --json`
- Optional: targeted `nix eval` for critical outputs
- When NixOS modules change, evaluate selected `nixosConfigurations` (prompt to choose, or use `NIX_REVIEW_TARGETS`)
5. **Lens Execution**
- Apply Phase 12 lenses by default (skip lens steps if lens directory is missing)
- Apply Phase 3 lenses when `--deep` is set
- Also run closure-impact when `flake.lock` changes (prompt before running unless `--deep`)
- Collect findings in standard format
6. **Synthesis + Review**
- Deduplicate and rank findings (group by root cause)
- Show summary + top issues
- Ask which findings to file (default recommendation: file all non-lint findings)
## Available Lenses
Lenses live in `~/.config/lenses/nix/`.
### Phase 1: Core Safety (Fast)
| Lens | Focus |
|------|-------|
| `determinism.md` | Unpinned fetchers, `import <nixpkgs>`, impure builtins (`getEnv`, `currentTime`) |
| `secrets.md` | Secrets embedded in Nix store or configs; sops/agenix misuse |
| `closure-hygiene.md` | Runtime vs build inputs, obvious bloat patterns |
### Phase 2: Reliability
| Lens | Focus |
|------|-------|
| `flake-hygiene.md` | Strict governance: duplicate inputs, missing `follows`, stale lockfiles |
| `module-safety.md` | Risky defaults, missing `mkIf`, missing assertions (pair with targeted eval) |
| `systemd-hardening.md` | Root services, missing hardening knobs |
### Phase 3: Architecture / Impact
| Lens | Focus |
|------|-------|
| `derivation-diff.md` | `nix-diff`/`nvd` explain what actually changed |
| `closure-impact.md` | Closure size deltas and dependency attribution (runs on `--deep` or when `flake.lock` changes) |
| `supply-chain.md` | Vulnerability scanning (sbomnix + grype) and input provenance |
## Static Tools
- **Lint**: `statix`
- **Unused**: `deadnix`
- **Format**: run only when repo declares formatter (treefmt/flake formatter/`nix fmt`)
- **Optional**: `flake-checker`, `nix-diff`, `nvd`, `sbomnix`, `grype`, `nix-tree`, `nix-du`
## Output
1. **Console summary** of findings by severity
2. **Interactive prompt** for issue filing (default: file all non-lint findings; lint findings recommend fix/skip)
3. **Beads issues** (if approved)
4. **Lens provenance** (note custom lenses if applicable)
## Auto-Fix Policy
No automatic fixes. Provide suggested commands for safe fixes (formatting, deadnix/statix).
## Configuration
Optional repo config file: `.nix-review.toml` (repo root).
```toml
# Example .nix-review.toml
formatter = "alejandra" # or "nixfmt-rfc-style"
eval = "auto" # auto|always|never
closure_impact = "auto" # auto|always|never
issue_filing = "non-lint" # all|non-lint|high-only
nixos_targets = ["nixosConfigurations.laptop", "nixosConfigurations.server"]
```
Notes:
- `eval = auto` runs `nix flake check` when `flake.nix` or `flake.lock` changes.
- `closure_impact = auto` runs when `flake.lock` changes (prompts unless `--deep`).
- `nixos_targets` overrides `NIX_REVIEW_TARGETS` and avoids prompts.
- Precedence: CLI flags > `.nix-review.toml` > environment variables.
## Lens Documentation
Lens files live in `~/.config/lenses/nix/`. Starter templates are in `skills/nix-review/lenses/`. See `skills/nix-review/LENSES.md` for the lens format and guidance.
## Requirements
- `statix`, `deadnix`, and a Nix formatter installed when the repo declares one
- `nix` CLI available
- Optional: `flake-checker`, `nix-diff`, `nvd`, `sbomnix`, `grype`, `nix-tree`
Tooling notes:
- Missing tools are skipped with a warning; the review continues with available checks.

View file

@ -0,0 +1,27 @@
# Nix Review Lenses
Starter lens templates for the nix-review skill.
## Available Lenses
| Lens | Focus |
|------|-------|
| `determinism.md` | Unpinned fetchers, impure builtins, `<nixpkgs>` imports |
| `secrets.md` | Plaintext secrets and unsafe secret handling |
| `closure-hygiene.md` | Runtime/build input hygiene, obvious bloat |
| `flake-hygiene.md` | Strict flake input governance |
| `module-safety.md` | NixOS/Home Manager module safety |
| `systemd-hardening.md` | Systemd hardening defaults |
| `derivation-diff.md` | Derivation/semantic change summaries |
| `closure-impact.md` | Closure size deltas and attribution |
| `supply-chain.md` | SBOM scan + CVE reporting |
## Output Convention
All lenses emit findings in the standard format:
```
[TAG] <severity:HIGH|MED|LOW> <file:line>
Issue: <whats wrong>
Suggest: <how to fix>
Evidence: <why it matters>
```

View file

@ -0,0 +1,23 @@
# Closure Hygiene Lens
## Purpose
Catch obvious dependency bloat and runtime/build input mistakes.
## Checks
- Heavy build tools in runtime closures (prefer `nativeBuildInputs` when possible).
- Runtime scripts calling host binaries instead of store paths.
- Large language/runtime toolchains pulled into minimal packages.
## Severity Guidance
- **MED**: runtime closure includes heavy toolchains.
- **LOW**: minor bloat or unclear tool placement.
## Examples
**Bad**
```nix
buildInputs = [ pkgs.gcc pkgs.python3 ];
```
**Good**
```nix
nativeBuildInputs = [ pkgs.gcc pkgs.python3 ];
```

View file

@ -0,0 +1,22 @@
# Closure Impact Lens
## Purpose
Measure closure size deltas and identify bloat sources.
## Checks
- `nix path-info -S` or `nix store diff-closures` on relevant outputs.
- Identify top contributors to growth.
## Severity Guidance
- **MED**: >10% closure growth or large absolute increases.
- **LOW**: small deltas with clear justification.
## Examples
**Bad**
```text
Closure grew by 120MB due to Python runtime
```
**Good**
```text
Closure grew by 5MB (expected) due to added locale data
```

View file

@ -0,0 +1,22 @@
# Derivation Diff Lens
## Purpose
Explain what actually changed in derivations or outputs.
## Checks
- Use `nix-diff` or `nvd` to summarize changes.
- Identify rebuild cascades or version bumps.
## Severity Guidance
- **MED**: unexpected rebuilds or large dependency changes.
- **LOW**: expected version bumps.
## Examples
**Bad**
```text
Rebuilds 300 derivations without explanation
```
**Good**
```text
nvd: openssl 3.0.12 -> 3.0.13; rebuilds 12 derivations
```

View file

@ -0,0 +1,25 @@
# Determinism Lens
## Purpose
Detect sources of impurity and unpinned inputs that break reproducibility.
## Checks
- `import <nixpkgs>` in flake-based repos.
- Unpinned fetchers (`fetchTarball`, `fetchGit`, `builtins.fetchTarball`) without hashes.
- Impure builtins (`builtins.getEnv`, `builtins.currentTime`, `builtins.currentSystem`) in non-dev contexts.
- Flake inputs using floating refs without lockfile updates.
## Severity Guidance
- **HIGH**: unpinned fetchers or `import <nixpkgs>` in production paths.
- **MED**: impure builtins in non-dev modules.
- **LOW**: devShell-only impurity.
## Examples
**Bad**
```nix
pkgs = import <nixpkgs> {};
```
**Good**
```nix
pkgs = inputs.nixpkgs.legacyPackages.${system};
```

View file

@ -0,0 +1,25 @@
# Flake Hygiene Lens
## Purpose
Enforce input governance and prevent dependency graph drift.
## Checks
- Duplicate `nixpkgs` inputs without `follows`.
- Missing `follows` for shared inputs.
- `flake.lock` out of sync with `flake.nix` changes.
## Severity Guidance
- **HIGH**: lockfile drift that changes inputs.
- **MED**: duplicate inputs without justification.
- **LOW**: minor governance drift.
## Examples
**Bad**
```nix
inputs.nixpkgs.url = "github:NixOS/nixpkgs";
inputs.nixpkgs-unstable.url = "github:NixOS/nixpkgs";
```
**Good**
```nix
inputs.nixpkgs-unstable.follows = "nixpkgs";
```

View file

@ -0,0 +1,24 @@
# Module Safety Lens
## Purpose
Ensure NixOS/Home Manager modules are safe, scoped, and guarded.
## Checks
- Enable flags that should be gated with `mkIf`.
- Missing `assertions` for critical invariants.
- Unscoped defaults that override user config without `mkDefault`.
## Severity Guidance
- **HIGH**: unsafe defaults that change system security.
- **MED**: missing `mkIf` guards.
- **LOW**: missing assertions or docs.
## Examples
**Bad**
```nix
config.services.nginx.enable = true;
```
**Good**
```nix
config = lib.mkIf cfg.enable { services.nginx.enable = true; };
```

View file

@ -0,0 +1,25 @@
# Secrets Lens
## Purpose
Prevent secrets from being embedded in Nix expressions or the Nix store.
## Checks
- Plaintext secrets in `environment.etc.*.text` or `systemd.services.*.environment`.
- `builtins.readFile` of secret paths that could be copied into the store.
- `sops-nix`/`agenix` misuse (unencrypted files referenced as secrets).
- Hardcoded tokens, passwords, or private keys in Nix files.
## Severity Guidance
- **HIGH**: plaintext secrets or keys in Nix expressions.
- **MED**: secrets in environment variables without restricted access.
- **LOW**: suspicious strings that need confirmation.
## Examples
**Bad**
```nix
environment.etc."api.key".text = "supersecret";
```
**Good**
```nix
sops.secrets."api.key" = {};
```

View file

@ -0,0 +1,24 @@
# Supply Chain Lens
## Purpose
Scan for known vulnerabilities and input provenance risks.
## Checks
- Generate SBOM with `sbomnix` and scan with `grype`.
- Identify CVEs across runtime closures.
- Flag untrusted or unexpected input sources.
## Severity Guidance
- **HIGH**: critical vulnerabilities without available fixes.
- **MED**: known CVEs with available fixes.
- **LOW**: informational advisories.
## Examples
**Bad**
```text
CVE-2025-1234 in openssl (no patch applied)
```
**Good**
```text
CVE-2025-1234 mitigated via nixpkgs patch; note in report
```

View file

@ -0,0 +1,24 @@
# Systemd Hardening Lens
## Purpose
Ensure systemd services are hardened where possible.
## Checks
- Services running as root without justification.
- Missing hardening options where applicable (`NoNewPrivileges`, `ProtectSystem`, `PrivateTmp`).
- Broad `CapabilityBoundingSet` or `AmbientCapabilities` usage.
## Severity Guidance
- **HIGH**: root services with network exposure.
- **MED**: missing hardening for services with file/network access.
- **LOW**: optional hardening improvements.
## Examples
**Bad**
```nix
serviceConfig.User = "root";
```
**Good**
```nix
serviceConfig = { User = "nginx"; NoNewPrivileges = true; };
```