Sample: made-up site. This is a sample report for shop.example, a site that does not exist. Every finding in it is made up.
Confidential: prepared for the owner of shop.example
This is a translation of signed yof report 5a3b1e00-0000-4000-8000-000000000000. The signed original is in English; its SHA-256 is below. Technical details (fixes, steps, references) are in English.
Scan the QR code or open https://yof.io/verify/5a3b1e00-0000-4000-8000-000000000000. The page checks yof's signature and shows only what the report's owner chose to share.
What this report is and isn't
This report is a check of shop.example at one point in time, as of 2026-11-07. It covers the repository, the dependencies and the live addresses named in its scope section, and nothing else.
It was made by automated rules and tests. Depending on the scope, yof read the code, checked the packages, tested the live site and, with the owner's consent, ran attack tests. The scope section lists exactly what was done and what was not.
It is evidence to support your security and compliance work. It is not a certification, a conformity assessment or legal advice, and it does not assess governance, policies, processes or other duties that are not technical.
A result with no findings does not mean the app has no vulnerabilities. Automated checks cannot find every flaw. Weaknesses nobody knows about yet (zero-days), flaws in business logic and anything outside the scope may remain, and the app may change after the date shown.
As far as the law allows, yof's liability for this report is limited to the price paid for it. Consumer law where you live may not allow some of these limits; where it does not, they do not apply to you.
Document control
Version history
Version 1, issued 2026-11-07
Requested by
The owner of shop.example, through the yof app (order 00000000).
Distribution
Confidential. Prepared for the owner of shop.example, who may share it with the people who need it, such as auditors, insurers or customers. Anyone holding a copy can check it with the QR code.
Open problems: 4. To fix now: 4, this week: 0, nice to have: 0.
Accepted risks (2) are listed in the appendix and are not counted as open.
Problems fixed since yof first saw them: 1.
Scope
Coverage: 45% of the parts yof can examine.
Outside: Tested in part
Inside the code: Tested in full
Pages behind a login: Not tested
Attack tests: Tested in full
AI assistant: Not tested
Every page: Not tested
Not tested
Pages behind a login were not tested: yof signs in only through the owner's own attack runs.
Only the first pages were read; the rest of the site was not.
AI assistant: not tested in this report.
What was checked and what was not
This states the reach of this report. A part that was not checked is not assumed to be fine.
Checked
Outside: Tested in part
Inside the code: Tested in full
Attack tests: Tested in full
Not checked, and why
Pages behind a login were not tested: yof signs in only through the owner's own attack runs.
Only the first pages were read; the rest of the site was not.
AI assistant: not tested in this report.
Never covered by automated checks
Flaws in business logic, such as a price or a step a person can skip.
Weaknesses nobody knows about yet (zero-days).
People and processes: phishing, passwords shared by hand, who has access.
Anything outside the scope named in this report, and any change after its date.
How sure yof is of each finding
Every finding carries one verdict. Only confirmed findings and findings that need validation are counted. Hardening notes and rejected findings are listed apart and never counted.
Confirmed: yof saw the impact happen, or holds the exact evidence (for example the key that was served, or the installed version an advisory names).
Needs validation: Likely, but not demonstrated: yof saw the pattern and could not prove the impact. A person or a deeper test needs to confirm it.
Rejected: Disproved by a second, separate check. Kept for the record, not counted.
What each severity means
Critical: Only for a confirmed finding that yof saw happen: data or control that belongs to someone else (another person's records, a whole table, a key with full power served to everyone), or a package known to be exploited or malicious.
High: Serious harm is likely if someone uses it: a confirmed exposure with limits, or a likely problem yof could not demonstrate. A finding that is not confirmed can reach high at most.
Medium: Makes an attack easier or its damage larger, under conditions such as a sign-in, a user's click or a specific setting.
Low: Small harm on its own, or reachable only in development and test tools.
Info: Worth knowing, no harm on its own.
Findings (5)
Open findings only, by verdict: confirmed (4), needs validation (0). Listed separately: hardening notes (1), rejected (0).
Fix now · Open
F-001 A package your app installs has a known serious vulnerability
Verdict: Confirmed. Severity: High. Why: The repository holds the exact evidence. yof did not see it on the live app, so it is rated high at most.
The weakness is public, with a published fix. Attackers scan for apps that still run the old version.
Where:package-lock.json:812 (example-shop/storefront at 0f1e2d3c4b5a)
Steps to see it:
Open package-lock.json:812 (example-shop/storefront at 0f1e2d3c4b5a).
Expected instead: The installed version of minimist is at or above 1.2.6.
Business impact: A published weakness in a component could be used against the app.
Fix: Upgrade minimist from 1.2.5 to 1.2.6 or newer (advisories: GHSA-xvch-5gv4-984h, CVE-2021-44906). Update package-lock.json with your package manager rather than by hand, run the install and the tests, and check whether the upgrade needs code changes.
CWE-1395: Dependency on Vulnerable Third-Party Component (https://cwe.mitre.org/data/definitions/1395.html)
OWASP A06:2021: Vulnerable and Outdated Components
First seen: 2026-10-30
Fix now · Open
F-002 Your Stripe secret key is in your page code
Verdict: Confirmed. Severity: Critical. Why: yof saw it on the live app.
Anyone can use it to take payments, give refunds and read your customers' payment details.
Where:https://shop.example/assets/app.js
Evidence (masked):sk_live_****9f2a
Steps to see it:
Open https://shop.example/assets/app.js without signing in.
What yof saw (masked): sk_live_****9f2a
Expected instead: No live Stripe secret key appears in code that visitors can download.
Business impact: Someone could sign in as another person or keep a session they should not have.
Fix: Remove the Stripe secret key (it starts with sk_live_) from all browser code and from any environment variable the browser can read. Create payments on the server only (an API route, server action or Supabase Edge Function) and send the browser just the client secret of the payment. The browser should only ever see the publishable key (pk_live_). Never paste the real key into this chat, never print it, and do not invent a replacement key. Tell me when the code no longer contains it, so I can create a new key in the provider's dashboard and delete the old one (the old one has been public, so it must be replaced).
References:
CWE-798: Use of Hard-coded Credentials (https://cwe.mitre.org/data/definitions/798.html)
OWASP A07:2021: Identification and Authentication Failures
First seen: 2026-10-30
Fix now · Open
F-003 Anyone can read a table in your database
Verdict: Confirmed. Severity: High. Why: yof saw it on the live app.
A stranger can download the rows in this table, such as your customers' names, emails or orders, without signing in.
Where:https://shop.example/
Evidence (masked):table orders: 3 rows readable without signing in
Steps to see it:
Open https://shop.example/ without signing in.
What yof saw (masked): table orders: 3 rows readable without signing in
Expected instead: Visitors who are not signed in cannot read this table.
Business impact: People could see or change data that belongs to someone else.
Fix: A Supabase table can be read by anyone with the public anon key. Turn on Row Level Security for this table (and every other table), then add policies so a signed-in person can only read their own rows (for example using auth.uid() = user_id). Remove any policy that allows the anon role to select everything. Write the SQL for me and explain what each policy allows. Never paste real customer data or keys into this chat.
-- for each table the browser can reach
alter table public.your_table enable row level security;
create policy "own rows" on public.your_table
for select using (auth.uid() = user_id);
F-004 One user can read another user's records by changing the id
Verdict: Confirmed. Severity: High. Why: yof saw it on the live app.
A test account opened a record of your other test account just by using its id, so real users can read each other's data.
Where:GET /api/orders/{id} (as user b)
Evidence (masked):200 with another customer's order
Steps to see it:
Sign in as test user b.
Send GET /api/orders/{id} with an id that belongs to another user or role.
What yof saw (masked): 200 with another customer's order
Expected instead: An account can open only its own records; another account's id answers 404 or 403.
Business impact: People could see or change data that belongs to someone else.
Fix: In my app, user B can read a record that belongs to user A by putting A's record id in the address (or sees it in a list). On the server, every query for a record or a list must be filtered by the signed-in user (or their workspace), for example `where id = :id and owner_id = :currentUser`, and answer 404 when the record is not theirs. If the database supports row-level security, turn it on with a policy that only allows the owner. Do not rely on ids being hard to guess.
References:
CWE-639: Authorization Bypass Through User-Controlled Key (https://cwe.mitre.org/data/definitions/639.html)
Verdict: Needs validation. Severity: Medium. Why: The check did not keep the evidence needed to confirm it.
Mail providers don't know what to do with messages that pretend to be you, so many of them get delivered.
Where:https://shop.example/
Steps to see it:
Look up the domain's DNS TXT records (for example: dig TXT shop.example).
Expected instead: The domain publishes a DMARC policy of quarantine or reject.
Business impact: A setting leaves an opening that is easy to find.
Fix: The domain has no DMARC record. Add a TXT record whose name (host) is exactly _dmarc, not @ (a DMARC record at the root is ignored), starting with monitoring: v=DMARC1; p=none; rua=mailto:<an address you read>. After a few weeks of clean reports, move to p=quarantine and then p=reject.
CWE-290: Authentication Bypass by Spoofing (https://cwe.mitre.org/data/definitions/290.html)
OWASP A05:2021: Security Misconfiguration
First seen: 2026-10-30
Hardening notes (not counted as findings)
Good practice that would add a layer of protection. yof demonstrated no risk, so these are not findings and are in no count.
H-001 Browsers aren't told to always use https: https://shop.example/ Add the Strict-Transport-Security header to every response, for example: Strict-Transport-Security: max-age=31536000; includeSubDomains. Set it in the host or framework headers config.
// vercel.json (or the same header in your host's settings)
{
"headers": [
{ "source": "/(.*)", "headers": [ { "key": "Strict-Transport-Security", "value": "max-age=63072000; includeSubDomains" } ] }
]
}
H-002 No rule limits which scripts can run: https://shop.example/ Add a Content-Security-Policy header that lists where scripts, styles, images and connections may come from. Start in Report-Only mode, check that the app still works, then enforce it.
// vercel.json (or the same header in your host's settings)
{
"headers": [
{ "source": "/(.*)", "headers": [ { "key": "Content-Security-Policy", "value": "default-src 'self'; frame-ancestors 'self'; object-src 'none'; base-uri 'self'" } ] }
]
}
Accepted risks and why
The owner looked at these findings and decided to accept the risk, for the reason given. They are not counted as open, and they are listed here so nothing is hidden.
H-002 No rule limits which scripts can run
Where
https://shop.example/
Reason
The payment widget needs inline scripts until its next version; we review this in January.
Accepted by
owner@shop.example
Accepted on
2026-11-05
Accepted in
the yof app
A Stripe test secret key is in your page code
Where
tests/fixtures/payments.ts
Reason
A Stripe test key in a test fixture; it cannot take real payments.
Accepted by
Sam (developer)
Accepted on
2026-11-02
Accepted in
the repository (.yof/accepted.json)
Glossary
Finding
Something yof saw that could let an attacker in or expose data, with where it is and how to see it.
Urgency
yof's plain order of work: fix now, fix this week, nice to have, for information.
CVSS 4.0
The common scoring system for how serious a weakness is, from 0 to 10, with the vector that explains the score.
CWE
The common list of weakness types; each finding names the type it belongs to.
Coverage
How much of the six parts yof can examine were tested in this report.
Accepted risk
A finding the owner decided to keep, with a reason. It is listed, not counted as open.
SBOM
Software bill of materials: the list of packages the app is built from.
Fingerprint
The SHA-256 hash of the report's content. Any change to the report changes it.
How to verify
Scan the QR code on the cover, or open the address printed under it. The page checks yof's signature against the register.
To check a file you were sent, choose it on that page: your browser works out its SHA-256 and compares it, the file is not uploaded.
With an Ed25519 signature, anyone can also check it offline with yof's public key, over the report ID, version, issue date and content hash.
Scan the QR code or open https://yof.io/verify/5a3b1e00-0000-4000-8000-000000000000. The page checks yof's signature and shows only what the report's owner chose to share.
Limits
A report shows evidence of what was tested on the dates above. It is not a promise about anything that was not tested, or about later changes.
Evidence is masked: keys, tokens, passwords and email addresses are never printed in full.