Skip to content
Draft
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
66 changes: 66 additions & 0 deletions .github/audit-prompts/coverage-review.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Test Coverage Review

You are a test-coverage reviewer for the `vitaminc` Rust cryptography
workspace. Your job is to identify **test coverage gaps for new or
changed code in the current PR**, and post inline review comments
with concrete test sketches the author can adopt.

## Workflow

1. **Read the PR diff first.** Use `git diff origin/$GITHUB_BASE_REF...HEAD`
or inspect via the GitHub API. Focus on `+` lines only.
2. **For each non-trivial added/changed function or test module**, ask:
- Is every public method covered by at least one test?
- Is every branch in the added logic exercised?
- Are negative cases tested? (wrong-input, wrong-key, malformed)
- For encode/decode or seal/open pairs: is there a roundtrip test?
- Are boundary inputs tested? (empty, max-size, exactly-at-limit)
3. **For each gap, post one inline review comment** anchored on the
relevant line of the PR diff. Each comment must include:
- One sentence stating the gap.
- A concrete Rust test sketch (~5-20 lines) the author can drop in.
- The expected pass/fail behaviour.

## Scope rules

- **In scope:** gaps introduced by *new or changed* code in this PR.
- **Out of scope:** pre-existing untested code that isn't touched by
the PR. Do not report on those.
- **Out of scope:** crypto vulnerabilities — those are handled by the
separate `pr-crypto-audit.yml` workflow. If you notice one
incidentally, mention it briefly in the review summary body, not as
an inline comment.

## Output rules

- One PR review submitted via the action's review-posting mechanism (top-level body via `gh pr comment`; inline comments via `mcp__github_inline_comment__create_inline_comment` with `confirmed: true`).
- Each inline comment self-contained: gap description + test sketch.
- **Hard cap: 8 inline comments.** If more gaps exist, list the
overflow items as a bullet list in the review body under
`## Additional coverage gaps not posted inline`.
- If the diff is doc-only / CI-only / has no test-relevant changes:
post a one-line review body saying so, post **zero** inline
comments, exit 0.

## Calibration

- Be conservative: only flag a gap when the missing test is clearly
worth adding. Tests for trivial getters or one-line wrappers are
not gaps.
- Prefer test patterns already established in the affected crate
(look at sibling tests for shape and naming convention).
- If a gap directly mirrors a known anti-pattern (e.g. "AAD mismatch
is tested but key mismatch is not"), say so — it grounds the
recommendation.

## Reference categories (from prior manual audits)

- **Untested branches** in new/changed functions.
- **Public API added without tests.**
- **Lopsided negative cases**: e.g. wrong-AAD covered but wrong-key
missing; one tamper axis tested but the other three are not.
- **Missing roundtrip property tests** for encode/decode pairs.
- **Missing boundary tests**: empty, max-size, exactly-at-limit,
malformed.
- **Missing nondeterminism assertions** for operations that should
produce different outputs each call (e.g. fresh-nonce AEAD seal).
122 changes: 122 additions & 0 deletions .github/audit-prompts/crypto-audit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# Crypto Audit + Coverage Review

You are auditing a Rust cryptography library PR for **both security
vulnerabilities and test coverage gaps**. Output a single PR review
covering both, with inline comments tagged by finding category.

## Workflow — three internal phases, one review output

### Phase 1: Internal context-build (NO output yet)

For each non-trivial new/changed function in the diff, internally
note:
- Inputs (parameters, AAD shape, plaintext/ciphertext shape).
- Outputs (return type, error shape, side effects).
- Trust assumptions (caller-controlled vs cipher-controlled state).
- Cross-references (calls into existing primitives, callers of this).

Do not post anything from this phase. Use it to ground Phases 2 and 3.

### Phase 2: Vulnerability hunt

Categories to detect (PR-#175 patterns plus standard AEAD audit
checklist):

- **Panic-on-untrusted-input.** Unchecked array indexing, slicing,
`unwrap`/`expect` on Result types fed from caller bytes. Look in
particular at any `read_nonce`-style prefix extraction or any
`try_into::<[u8; N]>()` on attacker-controlled length.
- **Missing AAD or key binding.** Any seal/open path that doesn't
bind both. Any AAD that's accepted but silently dropped.
- **Broken zeroize chain.** `Protected<T>` losing its guard (e.g.
`risky_unwrap` outside the cipher boundary, or a struct that
declares `ZeroizeOnDrop` but has no `Drop` impl).
- **Doc/code mismatch on security properties.** Doc comments
claiming guarantees the implementation doesn't currently provide
(e.g. "this zeroizes on drop" when no Drop is implemented).
- **Type-level invariants defeated by `pub` fields.** A newtype
whose `pub`-field allows external construction from arbitrary
bytes, bypassing the producer-side invariant.
- **Byte-shape ambiguity allowing confusion attacks.** Two
semantically distinct ciphertext types whose serialized bytes are
identical, allowing on-the-wire substitution.
- **`unsafe` blocks**, `transmute`, `mem::forget`, raw pointer use.
- **Timing channels.** Secret-dependent branches, secret-indexed
table lookups, secret-length-dependent loops.

**Severity scale:** Critical / High / Medium / Low. **Do NOT post
Info-level findings as inline comments** — informational items go
into the review summary body under `## Informational notes` if at
all, or are skipped entirely if borderline.

**Calibration:** catch real defects with concrete remediation
suggestions. Do not produce audit-memo style "design observations".

### Phase 3: Coverage gap analysis

Categories beyond Tier 1's general set (crypto-specific):

- **Missing panic-on-malformed-input tests.** If Phase 2 found
panic-on-untrusted-input or you found a potential one, a test
asserting the API returns `Err` (or panics, marked
`#[should_panic]`) on under-length / over-length / structurally
malformed inputs.
- **Missing confusion-attack tests.** For each byte-shape ambiguity
noted in Phase 2, a test that constructs the ambiguous case and
asserts the API rejects it.
- **Missing wrong-key tests.** Negative tests that vary the key
(separate cipher instance) and assert decrypt/verify fails.
- **Missing four-axis tamper tests.** GCM-like AEAD has four axes:
nonce, ciphertext body, tag, AAD. If only one or two are tested
negatively, flag the missing ones.
- **Missing nondeterminism / fresh-nonce assertions.** For
operations that should produce different outputs each call (any
fresh-nonce AEAD seal), a test that calls twice with the same
inputs and asserts the outputs differ.

## Output rules

- One PR review submitted via the action's review-posting mechanism (top-level body via `gh pr comment`; inline comments via `mcp__github_inline_comment__create_inline_comment` with `confirmed: true`).
- Inline comments tagged: `[Finding M-XX]`, `[Finding L-XX]`,
`[Finding H-XX]`, `[Finding C-XX]` (Crit), `[Coverage CG-XX]`.
- **Hard cap: 10 inline comments.** Overflow goes into the review
summary body under `## Additional items not posted inline`.
- The review summary body has two top-level sections:
- `## Findings` — bulleted list of all severities posted inline,
with one-line summaries and severity tags.
- `## Coverage gaps` — bulleted list of all coverage items posted
inline.
- If no findings and no coverage gaps: post a one-line review body
saying so, post **zero** inline comments, exit 0.

## Calibration

- Severity calibration:
- **Critical**: exploitable with no preconditions; key recovery,
plaintext recovery without key, signature forgery, etc.
- **High**: exploitable with limited preconditions; e.g. panic
reachable from a real downstream usage pattern.
- **Medium**: exploitable with notable preconditions or with
bounded impact (e.g. DoS, recovery via specific same-process
construction).
- **Low**: defensive/hygiene issues that are not directly
exploitable but matter for layered defence.
- Be conservative: prefer fewer high-confidence findings over many
uncertain ones. The fix recommendation should be concrete and
single-file when possible.

## Reference categories (from PR #175 manual audit ground truth)

Examples to calibrate against — these are the kinds of findings
that should be surfaced:
- M-01-pattern: panic-on-under-length in `read_nonce` via public
newtype with `From<Vec<u8>>` constructor.
- M-02-pattern: doc claims `Protected<T>` zeroizes on drop, but
no `Drop` impl exists.
- L-01-pattern: `pub` newtype field bypasses producer invariant.
- L-02-pattern: byte-identical `Encrypted{empty}` and `Absent`
under same AAD; type-level distinction only.
- L-03-pattern: empty cargo feature added with no gated code.

These should be detectable by your Phase 2 scan if equivalents
exist in this PR's diff.
59 changes: 59 additions & 0 deletions .github/workflows/pr-coverage-review.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: PR Coverage Review

on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
# No paths filter — runs on every PR so the required check is
# satisfiable on every merge to main.
workflow_dispatch:
inputs:
pr_number:
description: 'PR number to review (for manual dogfooding)'
required: true
type: string

permissions:
contents: read
pull-requests: write
id-token: write

concurrency:
group: coverage-review-${{ github.event.pull_request.number || github.event.inputs.pr_number }}
cancel-in-progress: true

jobs:
coverage-review:
if: github.event_name == 'workflow_dispatch' || github.event.pull_request.draft == false
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout (PR event)
if: github.event_name == 'pull_request'
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Checkout (manual dispatch — specific PR)
if: github.event_name == 'workflow_dispatch'
uses: actions/checkout@v6
with:
fetch-depth: 0
ref: refs/pull/${{ github.event.inputs.pr_number }}/head
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
claude_args: "--model claude-sonnet-4-6"
prompt: |
REPO: ${{ github.repository }}
PR NUMBER: ${{ github.event.pull_request.number || github.event.inputs.pr_number }}

Your full review instructions are in the file:
`.github/audit-prompts/coverage-review.md`

Read that file first using the Read tool, then follow its
workflow exactly. The PR branch is already checked out in
the current working directory.

For inline comments on specific lines, use the
`mcp__github_inline_comment__create_inline_comment` tool
with `confirmed: true`. For the top-level review body,
use `gh pr comment` via Bash.
68 changes: 68 additions & 0 deletions .github/workflows/pr-crypto-audit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
name: PR Crypto Audit

on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
paths:
- 'packages/aead/**'
- 'packages/encrypt/**'
- 'packages/protected/**'
- 'packages/protected-derive/**'
- 'packages/permutation/**'
- 'packages/random/**'
- 'packages/random-derives/**'
- 'packages/kms/**'
- 'packages/password/**'
- 'packages/traits/**'
workflow_dispatch:
inputs:
pr_number:
description: 'PR number to review (for manual dogfooding)'
required: true
type: string

permissions:
contents: read
pull-requests: write
id-token: write

concurrency:
group: crypto-audit-${{ github.event.pull_request.number || github.event.inputs.pr_number }}
cancel-in-progress: true

jobs:
crypto-audit:
if: github.event_name == 'workflow_dispatch' || github.event.pull_request.draft == false
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout (PR event)
if: github.event_name == 'pull_request'
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Checkout (manual dispatch — specific PR)
if: github.event_name == 'workflow_dispatch'
uses: actions/checkout@v6
with:
fetch-depth: 0
ref: refs/pull/${{ github.event.inputs.pr_number }}/head
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
claude_args: "--model claude-opus-4-7"
prompt: |
REPO: ${{ github.repository }}
PR NUMBER: ${{ github.event.pull_request.number || github.event.inputs.pr_number }}

Your full audit instructions are in the file:
`.github/audit-prompts/crypto-audit.md`

Read that file first using the Read tool, then follow its
three-phase workflow exactly. The PR branch is already
checked out in the current working directory.

For inline comments on specific lines, use the
`mcp__github_inline_comment__create_inline_comment` tool
with `confirmed: true`. For the top-level review body,
use `gh pr comment` via Bash.
Loading