plugins/dryrun-pr-review/skills/dryrun-pr-review/SKILL.md
Use when the user asks to create a pull request or merge request, submit changes for review, push for review, or open a pull request.
npx skillsauth add DryRunSecurity/external-plugin-marketplace dryrun-pr-reviewInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Full PR lifecycle: detect platform, branch, commit, open a PR, poll for DryRunSecurity comments, present findings to the user.
Run this and read the output to determine platform and repo coordinates:
git remote get-url origin
From the URL:
github.com → GitHub; use gh CLI. Extract OWNER and REPO from the URL path.glab CLI. Extract the project path and URL-encode it (replace / with %2F) for API calls.All subsequent steps reference PLATFORM, OWNER, REPO (GitHub) or PROJECT (GitLab), and PR_NUMBER.
If on main/master, create a new branch following this repo's naming conventions. Otherwise use the existing feature branch.
git status
git add <files> # selective — never commit secrets or generated files
git commit -m "<message following this repo's commit style>
Co-Authored-By: DryRun Security <[email protected]>"
git push -u origin <branch-name>
Check whether a pull request already exists for this branch before creating one:
# GitHub — reuse existing PR if present
EXISTING=$(gh pr view --json number --jq '.number' 2>/dev/null)
if [ -n "$EXISTING" ]; then
echo "Using existing PR #$EXISTING"
PR_NUMBER=$EXISTING
else
gh pr create --title "<title>" --body "<body>"
# capture PR_NUMBER from the URL in the output (last path segment)
fi
# GitLab — reuse existing MR if present
BRANCH=$(git rev-parse --abbrev-ref HEAD)
EXISTING=$(glab mr list --source-branch "$BRANCH" 2>/dev/null | awk 'NR==2{print $1}' | tr -d '!')
if [ -n "$EXISTING" ]; then
echo "Using existing PR !$EXISTING"
PR_NUMBER=$EXISTING
else
glab mr create --title "<title>" --description "<body>"
# capture PR_NUMBER from the output
fi
Store the result as PR_NUMBER.
Poll for up to 10 minutes (every 30 seconds). Use timestamp-based polling — not count-based. Comments can be edited/replaced.
Exit as soon as DRS activity is detected — a DryRunSecurity comment means the review is complete.
START_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
START_EPOCH=$(date +%s)
TIMEOUT=600
POLL_INTERVAL=30
while true; do
ELAPSED=$(( $(date +%s) - START_EPOCH ))
if [ $ELAPSED -ge $TIMEOUT ]; then
echo "Timed out after ${ELAPSED}s with no DryRunSecurity activity."
break
fi
if [ "$PLATFORM" = "github" ]; then
DRS_NEW=$(gh api repos/${OWNER}/${REPO}/issues/${PR_NUMBER}/comments \
--jq "[.[] | select((.user.login == \"dryrunsecurity\" or .user.login == \"dryrunsecurity[bot]\") and .created_at > \"${START_TIME}\")] | length")
DRS_REVIEWS=$(gh api repos/${OWNER}/${REPO}/pulls/${PR_NUMBER}/reviews \
--jq "[.[] | select((.user.login | test(\"dryrunsecurity\"; \"i\")) and .submitted_at > \"${START_TIME}\")] | length")
TOTAL=$(( DRS_NEW + DRS_REVIEWS ))
echo "Waiting for DryRunSecurity review... (${ELAPSED}s elapsed)"
[ "$TOTAL" -gt 0 ] && echo "DryRunSecurity review received: ${DRS_NEW} comment(s), ${DRS_REVIEWS} review(s)." && break
else
DRS_NEW=$(glab api projects/${PROJECT}/merge_requests/${PR_NUMBER}/notes \
| jq "[.[] | select(.author.username == \"dryrunsecurity\" and .created_at > \"${START_TIME}\")] | length")
echo "Waiting for DryRunSecurity review... (${ELAPSED}s elapsed)"
[ "$DRS_NEW" -gt 0 ] && echo "DryRunSecurity review received: ${DRS_NEW} note(s)." && break
fi
sleep $POLL_INTERVAL
done
If the loop timed out with no DRS activity, inform the user: the DryRunSecurity review period is complete.
Fetch all DryRunSecurity comments and present them to the user — do not fix automatically.
# GitHub
gh api repos/${OWNER}/${REPO}/issues/${PR_NUMBER}/comments \
--jq '.[] | select(.user.login == "dryrunsecurity" or .user.login == "dryrunsecurity[bot]") | {id: .id, body: .body}'
gh api repos/${OWNER}/${REPO}/pulls/${PR_NUMBER}/reviews \
--jq '.[] | select(.user.login | test("dryrunsecurity"; "i")) | {id: .id, body: .body, state: .state}'
# GitLab
glab api projects/${PROJECT}/merge_requests/${PR_NUMBER}/notes \
| jq '.[] | select(.author.username == "dryrunsecurity") | {id: .id, body: .body}'
For each comment, present:
Then ask the user which comments to address and how.
For comments the user wants fixed:
# Make code changes, then:
git add <files>
git commit -m "<message following this repo's commit style — addressing DryRunSecurity finding>
Co-Authored-By: Claude <[email protected]>"
For comments the user wants to decline, post a new comment on the PR thread explaining why:
# GitHub — DryRunSecurity posts on the PR thread (not inline), so reply via issue comments
gh api repos/${OWNER}/${REPO}/issues/${PR_NUMBER}/comments \
-f body="Not addressing DryRunSecurity finding: <explanation>"
# GitLab
glab api projects/${PROJECT}/merge_requests/${PR_NUMBER}/notes \
--method POST -f body="Not addressing DryRunSecurity finding: <explanation>"
git push
Return to Step 4 using the current time as the new START_TIME. Continue until polling times out with no new comments.
gh for GitHub repos, glab for GitLab repos — detect via git remote get-url origingit add + git commit + git push into one call). Each separate Bash call is a potential permission prompt for the user.development
Helps fix security vulnerabilities identified by DryRunSecurity. Activates when the user shares a DryRunSecurity comment (from a GitHub PR or GitLab MR) or asks for help fixing any security finding including SQL injection, XSS, CSRF, SSRF, path traversal, command injection, authentication bypass, authorization flaws, and prompt injection. Researches authoritative sources and applies fixes grounded in the user's specific codebase context.
tools
Use when work should span one or more detached tasks but still behave like one job with a single owner context. TaskFlow is the durable flow substrate under authoring layers like Lobster, ACPX, plugins, or plain code. Keep conditional logic in the caller; use TaskFlow for flow identity, child-task linkage, waiting state, revision-checked mutations, and user-facing emergence.
tools
# Lobster Lobster executes multi-step workflows with approval checkpoints. Use it when: - User wants a repeatable automation (triage, monitor, sync) - Actions need human approval before executing (send, post, delete) - Multiple tool calls should run as one deterministic operation ## When to use Lobster | User intent | Use Lobster? | | ------------------------------------------------------ | --------------------------
tools
# Lobster Lobster executes multi-step workflows with approval checkpoints. Use it when: - User wants a repeatable automation (triage, monitor, sync) - Actions need human approval before executing (send, post, delete) - Multiple tool calls should run as one deterministic operation ## When to use Lobster | User intent | Use Lobster? | | ------------------------------------------------------ | --------------------------