How we grade your app
Every scan ends with a score from 0 to 100 and a letter. This page shows the exact rules behind both, what the scan can and cannot see, and what to do when you think a finding is wrong.
Scope Passive external check; application logic (SQLi, IDOR) is not tested.
The short version
Every scan starts at 100. Each finding takes points off according to its severity and its category, and the result is rounded to a whole number between 0 and 100. A confirmed critical or high finding then caps the score, so one serious problem cannot hide behind a clean report. The letter comes straight from the score.
A good grade means nothing we can see from outside looks wrong. It does not mean the app is secure: we do not log in and do not test your application logic.
What we check
Shypset runs 100+ non-intrusive checks against the URL you give it. Without verification we only do what a browser or a public lookup would do:
- the page and the scripts, styles and source maps it loads: leaked API keys, vulnerable library versions, risky code patterns, the platform your app was built with;
- response headers and cookies;
- TLS: certificate, protocol versions and configuration;
- DNS and email records: SPF, DKIM, DMARC, MTA-STS, BIMI, CAA, DNSSEC;
- public records: certificate transparency logs, domain age (RDAP) and web archives;
/.well-known/security.txt.
After you verify that you own the domain (free on every plan) we also run deeper checks: common sensitive paths such as .env, .git, admin panels and backups, robots.txt and sitemap paths, unusual HTTP methods, header injection, GraphQL introspection, open redirects, rate limits, requests to subdomains found in certificate logs, and whether the Supabase tables your app references, or your Firebase database, can be read without logging in.
Findings are grouped as issues (confirmed problems), hardening (best-practice gaps that are not a hole on their own) and info (facts about the site). See the check catalog for the most important checks in detail.
How severity lowers the score
Findings are grouped by category (for example security_headers or cookies). Inside a category, the first finding of a severity takes its full weight; every further finding of the same severity adds 12% of that weight, up to a cap per severity.
| Severity | First finding | Each further one | Most per category | What it means |
|---|---|---|---|---|
| critical | −20 | −2.4 | −35 | Confirmed exposure: a live secret key, a readable .env or .git, a key with full database rights. |
| high | −10 | −1.2 | −20 | A real weakness we observed, such as a missing Content-Security-Policy header, or no DMARC record on a domain that handles email. |
| medium | −4 | −0.5 | −10 | A gap that makes an attack easier but is not a hole on its own. |
| low | −1.5 | −0.2 | −5 | Hardening and hygiene, such as a missing Referrer-Policy or Permissions-Policy header, or no MTA-STS policy for a domain that receives email. |
| info | 0 | 0 | 0 | A fact about your site. Shown for context, never counted. |
One category takes off at most 40 points. Categories are then sorted from the worst, and each next one counts a little less: the second 90%, the third 81%, and so on. Many small gaps still add up, but ten missing headers do not weigh like ten leaked keys.
Some categories are guesses or background facts rather than confirmed problems, so they move the score less. Their severity in the report stays as it is:
- ×0.5 heuristic or indirect signals that point at a possible problem without confirming it:
api_security,content_injection,crypto_weakness,dangerous_patterns,postmessage_security,privacy_compliance,third_party_risk,third_party_scripts. - ×0.25 reconnaissance and hygiene facts that describe the site rather than a hole:
historical_exposure,info_disclosure,subdomain_discovery,technology_disclosure,waf_detection.
Before scoring, some findings are merged, left out or turned into info:
- duplicates: when two checks report the same root cause (two SPF checks, a missing X-Frame-Options and a clickjacking warning), it counts once, at the higher severity;
- checks that could not run (a third-party service was down, a timeout): they are left out of the report and the score instead of counting against you;
- DNS and email findings on a platform subdomain such as
*.lovable.appor*.vercel.app: the platform manages those records, not you, so they are shown as info; - unconfirmed code patterns (for example
innerHTMLor__proto__seen in your JavaScript bundle, reported as “Possible …”) are folded into one info finding, “Code Patterns Worth a Manual Review”: it lists them for a manual review and never counts; - the overall “Security Headers Score: N/100” summary is info: the missing headers it sums up are already counted one by one.
What caps a grade
- Any critical finding caps the score at 25: that is an F, however clean the rest is.
- A confirmed high finding caps the score at 79: a B at best. A high counts as confirmed when its category has full weight and the finding is not phrased as “Possible …”.
- high Missing Content-Security-Policy Header
security_headers - medium Session Cookie Without Secure Flag
cookies - medium Session Cookie Without HttpOnly Flag
cookies
How it adds up
security_headers −10 cookies −4.5 × 0.9 = −4 score 100 −10 −4 ≈ 86 high cap 79 → grade B
From score to letter
| Grade | Score |
|---|---|
| A+ | 90–100 |
| A | 80–89 |
| B | 70–79 |
| C | 60–69 |
| D | 40–59 |
| F | 0–39 |
The grade belongs to one scan at one moment. When we recalibrate the scanner, old scans keep their grade; rescan to see the current one.
What we do not check
- Anything behind a login. We never sign in or submit credentials, so pages and APIs that need an account are not tested.
- Application logic. SQL injection, IDOR (reading another user’s data by changing an id), broken access rules inside your API and business-logic bugs need tests with real accounts. We do not run them.
- Your source code and repository. We see only what your live site serves to a visitor.
- Deeper probes without verification. Path probing and the live Supabase / Firebase check run only after you verify the domain.
- Every table in your database. The live check reads only the tables your app references, and we do not store the rows it sees.
Disputing a finding
Scanners make mistakes, and we would rather hear about them. When you are signed in, open the finding in your report and choose Report a false positive. You can add a short comment (up to 500 characters) about what we got wrong; one report per finding is enough.
We review reports and use the confirmed ones to fix or tune the check behind them. A report does not change the grade of the scan you reported. If you cannot sign in, write to us through the contact form.
See your own grade
Paste a URL. No code access, no setup.
Scan your app