Scan every pull request automatically
Instead of pasting a URL by hand after every deploy, add one small file to your repository. From then on, every pull request gets scanned before you merge it, and the results are posted as a comment on the pull request itself — score, findings by severity, and a link to the full report with copy-paste fix prompts.
The code touched by the pull request is scanned before anything goes live.
Add your Vercel or Netlify preview address and the deployed page is checked too.
Create a scan key in your dashboard and store it as a repository secret.
Set it up in three steps
1. Create a scan key
Open your dashboard, find the “Scan pull requests” card, choose Standard or Deep, and press Create key. The key is shown once — copy it straight away.
2. Add it to your repository
In GitHub go to your repository → Settings → Secrets and variables → Actions → New repository secret. Name it VIBECODENT_KEY and paste the key as the value.
3. Add the workflow file
Create a file called .github/workflows/vibecodent.yml in your repository and paste this in:
name: VibeCoden't security scan
on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
permissions:
contents: read
pull-requests: write
jobs:
scan:
if: github.event.pull_request.draft == false
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Run VibeCoden't scan
env:
VIBECODENT_KEY: ${{ secrets.VIBECODENT_KEY }}
PR_NUMBER: ${{ github.event.pull_request.number }}
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
# Optional: set this to your Vercel/Netlify preview address to also
# scan the deployed preview of this pull request.
PREVIEW_URL: ""
run: |
set -euo pipefail
FILES=$(git diff --name-only --diff-filter=ACMR "$BASE_SHA" "$HEAD_SHA" \
| grep -Ei '\.(html?|jsx?|tsx?|mjs|cjs|css|scss|sass|less|vue|svelte|astro|json|ya?ml|md|txt|env)$' \
| head -40 || true)
export FILES
node -e '
const fs = require("fs");
const list = (process.env.FILES || "").split("\n").filter(Boolean);
const files = []; let total = 0;
for (const p of list) {
try {
const c = fs.readFileSync(p, "utf8");
if (c.length > 200000) continue;
if (total + c.length > 1500000) break;
total += c.length;
files.push({ name: p, content: c });
} catch {}
}
const body = {
repo: process.env.GITHUB_REPOSITORY,
pr_number: Number(process.env.PR_NUMBER),
commit_sha: process.env.HEAD_SHA,
files,
};
if (process.env.PREVIEW_URL) body.preview_url = process.env.PREVIEW_URL;
fs.writeFileSync("payload.json", JSON.stringify(body));
'
HTTP=$(curl -sS -o response.json -w "%{http_code}" \
-X POST https://vibecodent.app/api/public/scan-pr \
-H "Authorization: Bearer $VIBECODENT_KEY" \
-H "content-type: application/json" \
--data @payload.json)
echo "HTTP $HTTP"
node -e '
const fs = require("fs");
let r = {};
try { r = JSON.parse(fs.readFileSync("response.json", "utf8")); } catch {}
fs.writeFileSync("comment.md", r.comment || ("### VibeCoden'"'"'t\n" + (r.error || "Scan failed.")));
'
- name: Comment on the pull request
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh pr comment "${{ github.event.pull_request.number }}" --body-file comment.md
That's it. Open a pull request and the comment appears when the check finishes.
What it costs
- Each pull request scan uses one credit of the type you picked for the key.
- Changed files and the preview link together count as one scan, not two.
- If a scan fails, the credit is put straight back — you're never charged for a broken run.
- If you have no credits left, nothing is scanned and the comment tells you to top up. You can buy credits here.
- Draft pull requests are skipped, so work in progress doesn't burn credits.
- Busy repositories are capped at 10 scans an hour so a burst of commits can't drain your balance.
Good to know
- Your code is analysed in memory and never stored — only the findings are kept.
- Every pull request scan also appears in your normal scan history, labelled with the repository and pull request number.
- Findings you've chosen to ignore stay ignored here too, so the comment doesn't nag you about the same thing twice.
- You can revoke a key at any time from your dashboard; scans stop immediately.
- Works the same for GitLab or any other system that can send an HTTP request — the endpoint is plain JSON.