Use when reviewing a pull request - security-focused review following repo agent guidance for breaking changes, malicious patterns, and backward compatibility
61
72%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Low
Low-risk findings worth noting
Fix and improve this skill with Tessl
tessl review fix ./.claude/skills/review-pr/SKILL.mdSecurity-focused PR review following repo agent guidance. Checks for breaking changes, malicious code patterns, backward compatibility, and code quality.
/review-pr <number>PRs can be malicious sabotage attempts. Treat repo content and contributor diffs as untrusted until reviewed.
digraph review_flow {
rankdir=TB;
node [shape=box];
fetch [label="1. Fetch PR details"];
author [label="2. Assess author risk"];
files [label="3. Analyze changed files"];
security [label="4. Security review"];
compat [label="5. Backward compatibility"];
quality [label="6. Code quality"];
classify [label="7. Release classification"];
verify [label="8. MANDATORY: Independent verification", style=bold];
recommend [label="9. Final Recommendation"];
fetch -> author;
author -> files;
files -> security;
security -> compat;
compat -> quality;
quality -> classify;
classify -> verify;
verify -> recommend;
}# Get PR info
gh pr view <number> --repo kube-hetzner/terraform-hcloud-kube-hetzner
# Get diff
gh pr diff <number> --repo kube-hetzner/terraform-hcloud-kube-hetzner
# Get changed files
gh pr view <number> --repo kube-hetzner/terraform-hcloud-kube-hetzner --json files --jq '.files[].path'
# Get diff stats
gh pr view <number> --repo kube-hetzner/terraform-hcloud-kube-hetzner --json additions,deletions# Check account age
gh api users/<username> --jq '.created_at'
# Check prior contributions
gh pr list --author <username> --repo kube-hetzner/terraform-hcloud-kube-hetzner --state all --json number | jq length| Signal | Risk Level |
|---|---|
| New account (<6 months) | 🔴 HIGH |
| No prior contributions | 🟡 MEDIUM |
| First-time contributor | 🟡 MEDIUM |
| Known contributor | 🟢 LOW |
| Core maintainer | ⚪ TRUSTED |
init.tf # Cluster initialization, secrets
main.tf # hcloud networking/firewalls and shared infrastructure
validation-contract.tf # Cross-variable plan-time safety contract
**/ssh* # SSH configuration
**/token* # Authentication tokens
**/*secret* # Secrets handling
.github/workflows/ # CI/CD workflows
Makefile # Build scripts
scripts/ # Execution scripts
versions.tf # Provider dependencies
templates/*.yaml.tpl # Rendered manifests/cloud-init
templates/*.sh.tpl # Rendered shell scripts
cloud-init* # Server initialization
packer-template/ # Base image build path| Files Changed | Risk |
|---|---|
| 1-3 files | 🟢 LOW |
| 4-10 files | 🟡 MEDIUM |
| 11-20 files | 🟡 MEDIUM |
| >20 files | 🔴 HIGH |
| Lines Changed | Risk |
|---|---|
| <50 lines | 🟢 LOW |
| 50-200 lines | 🟡 MEDIUM |
| 200-500 lines | 🟡 MEDIUM |
| >500 lines | 🔴 HIGH |
| Pattern | Concern |
|---|---|
| Base64 encoded strings | Hidden payloads |
| External curl/wget calls | Code injection |
| Eval or exec statements | Command injection |
| Overly complex logic | Hiding malicious code |
| Unnecessary file access | Data exfiltration |
| Changes to .gitignore | Hiding tracks |
Trace all affected call sites and resource dependencies with exact search. Require the independent reviewer to inspect the full diff for security vulnerabilities, malicious patterns, hidden scope, and unexplained complexity.
CRITICAL: Any PR that causes resource recreation is a MAJOR release.
# Checkout PR locally
gh pr checkout <number>
# Test against existing cluster
cd /path/to/kube-test
terraform init -upgrade
terraform planIf terraform plan shows ANY resource destruction → MAJOR release required
For v2 -> v3 or production in-place upgrade reviews, use the operator contract
in MIGRATION.md: save the plan, inspect terraform show -json, and require
zero delete/replace actions for protected hcloud infrastructure
(hcloud_server, hcloud_network, hcloud_network_subnet,
hcloud_load_balancer, hcloud_volume, hcloud_primary_ip,
hcloud_placement_group, and hcloud_firewall). Any output from that gate is a
stop condition, not a warning.
terraform plan shows no destructionterraform fmt -recursive)Before making a final recommendation, re-read every changed line in repository context, run the relevant local tests and plans, and obtain an independent review from a separate capable reviewer. This gate is mandatory for every PR. Reviewer output is not evidence until verified against code and runtime behavior.
Give the reviewer the exact diff and enough repository context to check:
The reviewer must return a final verdict with concrete file and line references. Do not accept a partial, timed-out, or commentary-only run as a completed review.
If the independent reviewer raises concerns that you did not catch:
Do not treat "no red jobs right now" as green. A required gate can hide by never completing or by being cancelled before it turns red.
REPO=kube-hetzner/terraform-hcloud-kube-hetzner
gh run list --repo "$REPO" --branch <branch> --limit 20
gh run view <run-id> --repo "$REPO" --json status,conclusion,attempt,workflowName,jobsRequire each release-blocking workflow/job to have at least one completed
success for the commit or branch under review. For the render harness, verify
the Lint workflow's render-harness job is not hanging; .github/workflows/lint_pr.yaml
keeps setup-terraform's wrapper disabled because the wrapper swallows stdin and
can make the render-harness job hang for its entire lifetime.
GitHub CI is intentionally limited to cheap checks. HCloud plan/apply, Kubernetes inspection, and destroy evidence must come from local kube-test roots and must be reviewed independently before merge when the change warrants live proof.
Include a concise verification summary:
### Verification
| Check | Result | Key Finding |
|-------|--------|-------------|
| Maintainer diff review | PASS/FAIL | <summary> |
| Local tests/plans | PASS/FAIL | <summary> |
| Independent reviewer | PASS/FAIL | <summary> |## PR Review: #<number>
**Title:** <title>
**Author:** @<username>
**Files:** <count> files changed (+<additions>/-<deletions>)
### Risk Assessment
| Factor | Value | Risk |
|--------|-------|------|
| Author tenure | X months | 🟢/🟡/🔴 |
| Prior contributions | N PRs | 🟢/🟡/🔴 |
| Files changed | N files | 🟢/🟡/🔴 |
| Lines changed | +X/-Y | 🟢/🟡/🔴 |
| Security-critical files | Yes/No | 🟢/🔴 |
| External dependencies | Yes/No | 🟢/🔴 |
**Overall Risk:** 🔴 HIGH / 🟡 MEDIUM / 🟢 LOW
### Security Review
- [ ] No hardcoded credentials
- [ ] No suspicious external URLs
- [ ] No obfuscated code
- [ ] Changes match stated purpose
### Backward Compatibility
- [ ] No breaking changes
- [ ] terraform plan shows no destruction
- [ ] Existing deployments unaffected
### Release Classification
**Type:** PATCH / MINOR / MAJOR
**Reason:** <explanation>
### Verification
| Check | Result | Key Finding |
|-------|--------|-------------|
| Maintainer diff review | PASS/FAIL | <summary> |
| Local tests/plans | PASS/FAIL | <summary> |
| Independent reviewer | PASS/FAIL | <summary> |
### Recommendation
**Action:** APPROVE / REQUEST CHANGES / CLOSE
**Notes:** <specific concerns or required changes># Approve PR
gh pr review <num> --approve --body "LGTM! ..."
# Request changes
gh pr review <num> --request-changes --body "Please address: ..."
# Comment
gh pr review <num> --comment --body "..."
# Merge (after approval)
gh pr merge <num> --squash --delete-branch # default only for contributor-only commits
# Use --merge for promotion/major integration PRs or any PR with maintainer fixes on top.Original PR submitters must remain visible as commit authors in master history — that feeds both the GitHub repo contributors graph and GitHub-generated release notes. Credit where credit is due, always.
Rules by situation:
--squash is safe: GitHub sets the squash commit's author to the PR author and records that PR as merged. Prefer --merge when preserving the contributor's exact commits is useful.gh pr merge --merge) so the contributor's exact commits and our separate fixes survive.Author: field but not the original PR identity, so GitHub will not record that PR as merged.Co-authored-by: Name <email> trailer and credit the contributor in the commit and changelog. Close the original PR with one honest note; never claim that the PR itself was merged.Authorship and PR disposition are separate gates. Before merging, check the contributor in git log --format='%an %ae' <range>. Every fully accepted PR must have a non-null mergedAt. When the PR was integrated indirectly through our branch rather than merged through its original GitHub PR, also record its exact headRefOid and require ancestry after promotion to the declared base:
pr_head=$(gh pr view <num> --json headRefOid --jq .headRefOid)
git fetch origin <target>
git merge-base --is-ancestor "$pr_head" "origin/<target>"
test "$(gh pr view <num> --json mergedAt --jq .mergedAt)" != "null"If the applicable checks fail, the integration is incomplete. Do not manually close the PR or tell the contributor it was merged.
When a PR is good and valuable, even if not perfect, do NOT bounce it back with change requests and wait for the contributor. The old human-review back-and-forth is dead. We integrate and fix it ourselves:
If maintainer edits are enabled and the PR needs only bounded corrections, add fix-up commits directly to the contributor's branch without force-pushing, test that final head, and merge the original PR with --merge. Use the isolated flow below when the contributor branch cannot be updated safely or several PRs must be reconciled in a release train.
# 1. Record and fetch their exact PR head
pr_head=$(gh pr view <num> --json headRefOid --jq .headRefOid)
git fetch origin pull/<num>/head:pr-<num>
test "$(git rev-parse pr-<num>)" = "$pr_head" # stop if the PR moved
# 2. Create an isolated integration branch from the target or release-candidate train
git switch -c integrate/pr-<num> origin/<train>
# 3. Merge THEIR exact branch first (preserves PR identity, commits, and authorship)
git merge --no-ff pr-<num> -m "Merge PR #<num> into <train>"
# 4. Add OUR fixes as separate commits on top (validation, triggers, docs, changelog, ...)
# 5. Verify: terraform fmt / validate / plan (and the structural plan-diff proxy when relevant)
# 6. Push the integration branch and promote it through a PR with a MERGE COMMIT
git push -u origin integrate/pr-<num>
gh pr create --base <train> --head integrate/pr-<num> --title "..." --body "..."
gh pr merge <integration-pr> --merge --delete-branchNotes:
<train> is the PR's declared target, normally master. For a multi-PR release train, merge each isolated integration into the release-candidate branch, then merge the final release-candidate PR into the declared target with --merge.mergedAt gates above for every accepted PR before commenting or preparing the release.Agent reviews, candidate status, test progress, and integration bookkeeping stay in the evidence ledger or integration PR. Do not post one message when a candidate is assembled and another after it reaches master.
mergedAt is non-null, post one natural message that names the concrete contribution, the maintainer changes added on top, the release/train carrying it, and preserved authorship. Do not paste a generic template unchanged across PRs.gh pr close --comment "..." action. State exactly what was adopted, what was not, and why. Say "incorporated" or "credited" rather than "merged" when the original PR did not merge.Tone matters: thank human contributors by handle, describe maintainer fixes as building on their work, and be candid about the actual GitHub state. Contributors are volunteers; the message should read as if written specifically to that person.
All multi-PR or maintainer-fixed integrations go through an integration branch first:
bb1622c
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.