docs
You paste a public GitHub repo. Shepherd reads your code, looks for the usual ways apps get hacked or break, and hands you a score with a list of what to fix. It takes about ten seconds. Here is what happens in that time:
Some checks, like finding leaked keys, run on every file no matter the language. Others are picked for the language of each file. For example, Shepherd looks for reentrancy in Solidity, unsafe blocks in Rust, and open CORS in your API code.
The score is one number from 0 to 100. Everybody starts at 100. Each thing Shepherd finds takes points away, and worse problems take more:
start: 100 points each critical: minus 16 each medium: minus 6 each low: minus 2 score = whatever is left (never below 0)
Quick example: one leaked key (critical) and two small things (low) gives 100 - 16 - 2 - 2 = 80. That lands in “Mostly Alive”.
We also show how many checks passed. A good repo lights up with passed checks, which is the honest way to show the score is earned.
All 45 checks Shepherd runs. This list is built straight from the scanner, so it is always what actually runs. Search it or filter by group.
Calling .unwrap() or .expect() crashes the whole program if the value is missing or an error. Fine in scripts, risky in a running service.
panic!() stops the program. In a server or library, one bad request can take the whole thing down.
CORS is set to '*', so any website can call your API from a user's browser.
This injects raw HTML into the page. If the HTML comes from a user, they can run scripts in your app (XSS).
eval() runs whatever string you give it as code. If any of that string comes from a user, they can run their own code.
eval() and exec() run strings as code. With any user input, that is remote code execution.
Running Flask with debug=True exposes an interactive debugger that can run code.
Running sh -c with a built string and user input can let people run their own commands.
Building a SQL string with Sprintf and user input lets attackers rewrite the query.
Runtime.exec with a string that includes user input can run extra commands.
Building SQL with + and user input lets attackers change the query.
eval() runs a string as PHP. With any user input it is full code execution.
Putting request variables directly into a SQL string is SQL injection.
pickle can run code while loading. Never use it on data from users or the network.
eval, instance_eval, and class_eval run strings as code. With user input that is remote code execution.
Putting user input inside system() or backticks can run extra shell commands.
exec() runs a shell command. If part of that command is user input, they can run their own commands on your server.
Building a command string with format! and user input can let people run their own commands.
A SQL query is built with string joining and user input. Attackers can rewrite the query (SQL injection).
Running a subprocess with shell=True and user input lets attackers run their own commands.
Skipping TLS checks means a fake server can pretend to be the real one and read your traffic.
yaml.load without SafeLoader can run code from a crafted file.
Your token setup allows the 'none' algorithm, which means a token with no signature is accepted. Anyone can forge a login.
transmute reinterprets bytes as another type with no checks. Easy to get wrong and cause memory bugs.
An unsafe block skips Rust's memory checks. Mistakes here can cause crashes or memory bugs that Rust normally prevents.
An Anthropic key (sk-ant-...) is in the code. Anyone can run up your bill with it.
An AWS access key ID is hard-coded. Paired with the secret, it opens your whole AWS account.
A GitHub personal access token is hard-coded. Depending on scope it can read and write your repos.
A Google or Firebase API key is hard-coded. Without restrictions it can be used by anyone.
A JWT signing secret looks hard-coded. If it leaks, anyone can forge logged-in sessions.
A MongoDB connection string with a username and password is in the code.
An OpenAI key (sk-...) is hard-coded. Anyone who reads this file can spend your money.
A Postgres connection string with credentials is hard-coded.
A private key block is committed. This can be an SSH, TLS, or signing key.
A chat webhook URL is hard-coded. People can send messages to your channel with it.
A live Stripe secret key is in the code. That is full access to charges, refunds, and customer data.
The Supabase service_role key skips all your security rules. Anyone with it can read or delete everything.
block.timestamp and blockhash can be influenced by whoever produces the block, so they are unsafe for lotteries or anything random.
delegatecall runs another contract's code with your contract's storage. If the target can be set by someone else, they own your contract.
A pragma like ^0.8.0 lets the contract compile with many versions. A different version can change behavior in subtle ways.
A low-level .call() returns whether it worked, but the result is being ignored. Failures pass silently and can break your logic.
Sending ETH with .call before updating balances lets an attacker call back in and drain funds (reentrancy).
selfdestruct can delete the contract and send its balance away. If anyone can trigger it, your contract can be wiped.
A function named like mint, withdraw, or setOwner looks public with no obvious access check. Anyone might be able to call it.
Using tx.origin to check who is calling can be tricked. A malicious contract in the middle passes the check and steals funds.
Being straight with you matters more than looking clever, so here is where Shepherd stops:
Shepherd looks at your code as text. It does not run your app, so it can miss bugs that only show up while running.
Private repos need a GitHub login, which is on the roadmap.
A clean score does not mean you cannot be hacked. It means the common traps are not obvious in your code.
If your app holds real money or sensitive data, also pay for a human security review. Shepherd is a great first pass, not the last word.
Shepherd reads your code through GitHub's public API and checks it in memory. When the scan is done, that code is gone. We do not save your files.
If you tick the box to join the Wall of Fame, we keep only a random ID, your score, the tier, the top issue type, and how many issues there were. Your repo name is not shown.
The scan history on the scan page lives in your browser, not on our servers.
Is this safe to run on my repo?
Yes. Shepherd only reads. It never writes to your repo, never opens a pull request, and never runs your code.
Why is it free?
Because we are building in public and watching apps blow up in production makes us sad. No catch.
My score is 23. Am I doomed?
No. Vibe has survived worse. Fix the critical items first, scan again, and watch the number climb.
Does a high score mean I am unhackable?
No. It means the common mistakes are not sitting in plain sight. Keep good habits and, for serious apps, get a human review too.
Which languages does it support?
Right now: JavaScript, TypeScript, Python, Rust, Solidity, Go, Ruby, PHP, Java. Secret detection works on any file.