{
  "schema": "yof-report/1",
  "lang": "en",
  "reportId": "5a3b1e00-0000-4000-8000-000000000000",
  "version": 1,
  "issuedAt": "2026-11-07T09:00:00.000Z",
  "kind": "full",
  "whiteLabel": false,
  "orderId": "00000000-0000-4000-8000-000000000000",
  "sections": [
    "cover",
    "about",
    "attestation",
    "summary",
    "method",
    "findings",
    "ai",
    "dependencies",
    "accepted",
    "appendix",
    "files"
  ],
  "cover": {
    "title": "Security report: Full",
    "subtitle": "Independent check of a web app by yof",
    "host": "shop.example",
    "sample": "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.",
    "url": "https://shop.example/",
    "date": "2026-11-07",
    "scopeLine": "shop.example: the outside of the app, the code of example-shop/storefront, attack results from the owner's CI.",
    "preparedBy": "Prepared with yof (yof.io)",
    "brand": null
  },
  "about": {
    "heading": "What this report is and isn't",
    "paragraphs": [
      "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."
    ]
  },
  "attestation": {
    "heading": "Attestation",
    "paragraphs": [
      "This letter records that yof ran the checks listed in this report against shop.example (https://shop.example/) between 2026-11-07 and 2026-11-07, for report 5a3b1e00-0000-4000-8000-000000000000, version 1, issued 2026-11-07.",
      "Scope: the outside of the app, the code of example-shop/storefront, attack results from the owner's CI. The owner proved control of the app before any deep check ran. Coverage: 45% of the six parts yof can examine (outside, inside the code, pages behind a login, attack tests, the AI assistant, every page).",
      "Method: each check follows the published method named next to it in section 3 (OWASP WSTG v4.2, OWASP ASVS 4.0.3, the OWASP Top 10 for LLM Applications 2025, and the DNS standards for email). Every request yof made is in the proof list in the appendix.",
      "Limits: findings describe the app as it was on those dates. Parts that were not tested are listed, never assumed. This letter is evidence of testing; it is not an audit opinion and makes no statement about anything outside its scope.",
      "Integrity: the SHA-256 hash of this report's content and its signature are on the last page. Anyone can check the report ID and hash against yof's public register; a changed copy will not match."
    ]
  },
  "summary": {
    "heading": "Executive summary",
    "risk": "high",
    "riskLine": "High: at least one problem should be fixed now.",
    "plain": [
      "yof tested shop.example from 2026-11-07. Coverage: 45% of the parts yof can examine.",
      "4 problems are open: 4 to fix now, 0 this week, 0 nice to have.",
      "Open findings only, by verdict: confirmed (4), needs validation (0). Listed separately: hardening notes (1), rejected (0).",
      "Accepted risks (2) are listed in the appendix and are not counted as open.",
      "1 problem was fixed since yof first saw it.",
      "Score over the last 2 checks: 58 then 71 (on what each check could test)."
    ],
    "top": [
      {
        "ref": "F-001",
        "title": "minimist 1.2.5 has a known serious vulnerability (package-lock.json)",
        "urgencyLabel": "Fix now",
        "place": "package-lock.json:812 (example-shop/storefront at 0f1e2d3c4b5a)"
      },
      {
        "ref": "F-002",
        "title": "Your Stripe secret key is in your page code (/assets/app.js)",
        "urgencyLabel": "Fix now",
        "place": "https://shop.example/assets/app.js"
      },
      {
        "ref": "F-003",
        "title": "Anyone can read a table in your database",
        "urgencyLabel": "Fix now",
        "place": "https://shop.example/"
      }
    ],
    "fixed": 1,
    "trend": [
      {
        "date": "2026-10-31",
        "score": 58,
        "coverage": 70
      },
      {
        "date": "2026-11-07",
        "score": 71,
        "coverage": 78
      }
    ],
    "score": 71
  },
  "method": {
    "heading": "Scope and method",
    "coveragePct": 45,
    "coverageParts": [
      {
        "part": "outside",
        "label": "Outside",
        "state": "Tested in part",
        "value": 0.6818181818181818
      },
      {
        "part": "inside",
        "label": "Inside the code",
        "state": "Tested in full",
        "value": 1
      },
      {
        "part": "logged_in",
        "label": "Pages behind a login",
        "state": "Not tested",
        "value": 0
      },
      {
        "part": "attacks",
        "label": "Attack tests",
        "state": "Tested in full",
        "value": 1
      },
      {
        "part": "ai",
        "label": "AI assistant",
        "state": "Not tested",
        "value": 0
      },
      {
        "part": "all_pages",
        "label": "Every page",
        "state": "Not tested",
        "value": 0
      }
    ],
    "period": {
      "from": "2026-11-07T08:30:00Z",
      "to": "2026-11-07T08:41:00Z"
    },
    "standards": [
      "ASVS V14.1",
      "ASVS V14.2.1",
      "ASVS V14.3",
      "ASVS V14.4",
      "ASVS V2.10.4",
      "ASVS V4.1",
      "ASVS V5",
      "ASVS V6.4.1",
      "OWASP A06:2021",
      "OWASP API Top 10 2023",
      "RFC 6376 (DKIM)",
      "RFC 7208 (SPF)",
      "RFC 7489 (DMARC)",
      "WSTG-ATHN-04",
      "WSTG-ATHZ-01",
      "WSTG-ATHZ-02",
      "WSTG-ATHZ-04",
      "WSTG-CLNT-09",
      "WSTG-CONF-03",
      "WSTG-CONF-04",
      "WSTG-CONF-05",
      "WSTG-CONF-06",
      "WSTG-CONF-07",
      "WSTG-CONF-12",
      "WSTG-INFO-05",
      "WSTG-INPV",
      "WSTG-SESS-01"
    ],
    "checks": [
      {
        "area": "Keys in page code",
        "status": "Ran in full",
        "things": 14,
        "method": [
          "WSTG-INFO-05",
          "ASVS V14.3"
        ],
        "what": "Read the code every visitor downloads and look for keys and passwords in it."
      },
      {
        "area": "Database access",
        "status": "Ran in full",
        "things": 4,
        "method": [
          "WSTG-ATHZ-02",
          "ASVS V4.1"
        ],
        "what": "Ask the app's own database service, without signing in, for rows a visitor should not see."
      },
      {
        "area": "Files that should be private",
        "status": "Ran in full",
        "things": 42,
        "method": [
          "WSTG-CONF-04",
          "WSTG-CONF-03"
        ],
        "what": "Ask for files that should never be public: backups, environment files, source maps, repository folders."
      },
      {
        "area": "Admin pages",
        "status": "Ran in full",
        "things": 9,
        "method": [
          "WSTG-CONF-05"
        ],
        "what": "Look for admin and debug pages that answer without a sign-in."
      },
      {
        "area": "Public API",
        "status": "Ran in part",
        "things": 6,
        "method": [
          "WSTG-CONF-06",
          "WSTG-ATHZ-02",
          "OWASP API Top 10 2023"
        ],
        "what": "Call the app's public API addresses as a stranger and read what they return."
      },
      {
        "area": "Browser protection headers",
        "status": "Ran in full",
        "things": 1,
        "method": [
          "WSTG-CONF-07",
          "WSTG-CONF-12",
          "WSTG-CLNT-09"
        ],
        "what": "Read the response headers for HTTPS enforcement, framing, content type and referrer rules."
      },
      {
        "area": "Email sending rules",
        "status": "Ran in full",
        "things": 2,
        "method": [
          "RFC 7208 (SPF)",
          "RFC 6376 (DKIM)",
          "RFC 7489 (DMARC)"
        ],
        "what": "Read the domain's DNS records for the rules that stop others sending mail as it."
      },
      {
        "area": "Website in a real browser",
        "status": "Ran in full",
        "things": 1,
        "method": [],
        "what": "Website basics. Not a security test."
      },
      {
        "area": "Packages",
        "status": "Ran in full",
        "things": 640,
        "method": [
          "ASVS V14.2.1",
          "OWASP A06:2021"
        ],
        "what": "Read every lockfile and ask OSV.dev about each package version."
      },
      {
        "area": "Keys in the code",
        "status": "Ran in full",
        "things": 214,
        "method": [
          "ASVS V2.10.4",
          "ASVS V6.4.1"
        ],
        "what": "Read every text file for keys, tokens and passwords."
      },
      {
        "area": "Configuration",
        "status": "Ran in full",
        "things": 214,
        "method": [
          "ASVS V14.1",
          "ASVS V14.4",
          "WSTG-CONF-12"
        ],
        "what": "Read Dockerfiles, CI workflows, hosting config, database migrations and access rules."
      },
      {
        "area": "Code",
        "status": "Ran in full",
        "things": 170,
        "method": [
          "ASVS V5",
          "WSTG-INPV"
        ],
        "what": "Read the code for injection, open redirects, unchecked tokens and weak password storage."
      },
      {
        "area": "Attack tests",
        "status": "Ran in full",
        "things": 1,
        "method": [
          "WSTG-ATHZ-01",
          "WSTG-ATHZ-02",
          "WSTG-ATHZ-04",
          "WSTG-ATHN-04",
          "WSTG-SESS-01"
        ],
        "what": "Results the owner's own CI uploaded from yof attack: signed-in requests as other users and roles. Uploaded 2026-11-06."
      }
    ],
    "notTested": [
      "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."
    ],
    "coverage": {
      "heading": "What was checked and what was not",
      "intro": "This states the reach of this report. A part that was not checked is not assumed to be fine.",
      "checkedHeading": "Checked",
      "checked": [
        {
          "part": "outside",
          "state": "partial",
          "line": "Outside: Tested in part"
        },
        {
          "part": "inside",
          "state": "full",
          "line": "Inside the code: Tested in full"
        },
        {
          "part": "attacks",
          "state": "full",
          "line": "Attack tests: Tested in full"
        }
      ],
      "notCheckedHeading": "Not checked, and why",
      "notChecked": [
        "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."
      ],
      "neverHeading": "Never covered by automated checks",
      "never": [
        {
          "id": "business_logic",
          "line": "Flaws in business logic, such as a price or a step a person can skip."
        },
        {
          "id": "zero_days",
          "line": "Weaknesses nobody knows about yet (zero-days)."
        },
        {
          "id": "people",
          "line": "People and processes: phishing, passwords shared by hand, who has access."
        },
        {
          "id": "outside_scope",
          "line": "Anything outside the scope named in this report, and any change after its date."
        }
      ]
    }
  },
  "findings": {
    "heading": "Findings",
    "counts": {
      "fix_now": 4,
      "fix_week": 0,
      "nice": 0,
      "info": 0
    },
    "open": 4,
    "items": [
      {
        "ref": "F-001",
        "fingerprint": "sample-inside.dep_vulnerable_now",
        "ruleId": "inside.dep_vulnerable_now",
        "title": "minimist 1.2.5 has a known serious vulnerability (package-lock.json)",
        "why": "The weakness is public, with a published fix. Attackers scan for apps that still run the old version.",
        "urgency": "fix_now",
        "urgencyLabel": "Fix now",
        "cvss": {
          "version": "4.0",
          "score": 9.3,
          "rating": "Critical",
          "vector": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N",
          "typical": true
        },
        "cwe": [
          "CWE-1395"
        ],
        "owasp": [
          "OWASP A06:2021 Vulnerable and Outdated Components"
        ],
        "evidence": null,
        "place": "package-lock.json:812 (example-shop/storefront at 0f1e2d3c4b5a)",
        "reproduction": [
          "Open package-lock.json:812 (example-shop/storefront at 0f1e2d3c4b5a).",
          "Expected instead: The installed version of minimist is at or above 1.2.6."
        ],
        "impact": "A published weakness in a component could be used against the app.",
        "expected": "The installed version of minimist is at or above 1.2.6.",
        "package": {
          "name": "minimist",
          "ecosystem": "npm",
          "installed": "1.2.5",
          "advisories": [
            {
              "id": "GHSA-xvch-5gv4-984h",
              "url": "https://github.com/advisories/GHSA-xvch-5gv4-984h"
            },
            {
              "id": "CVE-2021-44906",
              "url": "https://nvd.nist.gov/vuln/detail/CVE-2021-44906"
            }
          ],
          "fixed": "1.2.6",
          "lockfile": "package-lock.json:812",
          "lockfiles": [],
          "manifests": [
            "package.json"
          ],
          "purl": "pkg:npm/minimist@1.2.5"
        },
        "fix": {
          "text": "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.",
          "code": null
        },
        "references": [
          "GHSA-xvch-5gv4-984h (https://github.com/advisories/GHSA-xvch-5gv4-984h)",
          "CVE-2021-44906 (https://nvd.nist.gov/vuln/detail/CVE-2021-44906)",
          "CWE-1395: Dependency on Vulnerable Third-Party Component (https://cwe.mitre.org/data/definitions/1395.html)",
          "OWASP A06:2021: Vulnerable and Outdated Components"
        ],
        "status": "open",
        "statusLabel": "Open",
        "acceptedReason": null,
        "acceptedBy": null,
        "acceptedAt": null,
        "firstSeen": "2026-10-30",
        "lastSeen": "2026-11-07",
        "fixedAt": null,
        "source": "inside",
        "verdict": "confirmed",
        "severity": "high",
        "verification": {
          "basis": "exact",
          "reasons": [
            "exact_evidence",
            "cap_not_seen_live"
          ],
          "why": [
            "The repository holds the exact evidence.",
            "yof did not see it on the live app, so it is rated high at most."
          ]
        },
        "at": {
          "file": "package-lock.json",
          "line": 812,
          "url": null
        }
      },
      {
        "ref": "F-002",
        "fingerprint": "sample-secret.stripe_live",
        "ruleId": "secret.stripe_live",
        "title": "Your Stripe secret key is in your page code (/assets/app.js)",
        "why": "Anyone can use it to take payments, give refunds and read your customers' payment details.",
        "urgency": "fix_now",
        "urgencyLabel": "Fix now",
        "cvss": {
          "version": "4.0",
          "score": 9.3,
          "rating": "Critical",
          "vector": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:H/SI:H/SA:H",
          "typical": false
        },
        "cwe": [
          "CWE-798"
        ],
        "owasp": [
          "OWASP A07:2021 Identification and Authentication Failures"
        ],
        "evidence": "sk_live_****9f2a",
        "place": "https://shop.example/assets/app.js",
        "reproduction": [
          "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."
        ],
        "impact": "Someone could sign in as another person or keep a session they should not have.",
        "expected": "No live Stripe secret key appears in code that visitors can download.",
        "fix": {
          "text": "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).",
          "code": null
        },
        "references": [
          "CWE-798: Use of Hard-coded Credentials (https://cwe.mitre.org/data/definitions/798.html)",
          "OWASP A07:2021: Identification and Authentication Failures"
        ],
        "status": "open",
        "statusLabel": "Open",
        "acceptedReason": null,
        "acceptedBy": null,
        "acceptedAt": null,
        "firstSeen": "2026-10-30",
        "lastSeen": "2026-11-07",
        "fixedAt": null,
        "source": "outside",
        "verdict": "confirmed",
        "severity": "critical",
        "verification": {
          "basis": "observed",
          "reasons": [
            "observed_live"
          ],
          "why": [
            "yof saw it on the live app."
          ]
        },
        "at": {
          "file": null,
          "line": null,
          "url": "https://shop.example/assets/app.js"
        }
      },
      {
        "ref": "F-003",
        "fingerprint": "sample-db.supabase_table_open",
        "ruleId": "db.supabase_table_open",
        "title": "Anyone can read a table in your database",
        "why": "A stranger can download the rows in this table, such as your customers' names, emails or orders, without signing in.",
        "urgency": "fix_now",
        "urgencyLabel": "Fix now",
        "cvss": {
          "version": "4.0",
          "score": 8.7,
          "rating": "High",
          "vector": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N",
          "typical": false
        },
        "cwe": [
          "CWE-862"
        ],
        "owasp": [
          "OWASP A01:2021 Broken Access Control"
        ],
        "evidence": "table orders: 3 rows readable without signing in",
        "place": "https://shop.example/",
        "reproduction": [
          "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."
        ],
        "impact": "People could see or change data that belongs to someone else.",
        "expected": "Visitors who are not signed in cannot read this table.",
        "fix": {
          "text": "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.",
          "code": {
            "lang": "sql",
            "body": "-- for each table the browser can reach\nalter table public.your_table enable row level security;\ncreate policy \"own rows\" on public.your_table\n  for select using (auth.uid() = user_id);"
          }
        },
        "references": [
          "CWE-862: Missing Authorization (https://cwe.mitre.org/data/definitions/862.html)",
          "OWASP A01:2021: Broken Access Control"
        ],
        "status": "open",
        "statusLabel": "Open",
        "acceptedReason": null,
        "acceptedBy": null,
        "acceptedAt": null,
        "firstSeen": "2026-10-30",
        "lastSeen": "2026-11-07",
        "fixedAt": null,
        "source": "outside",
        "verdict": "confirmed",
        "severity": "high",
        "verification": {
          "basis": "observed",
          "reasons": [
            "observed_live"
          ],
          "why": [
            "yof saw it on the live app."
          ]
        },
        "at": {
          "file": null,
          "line": null,
          "url": "https://shop.example/"
        }
      },
      {
        "ref": "F-004",
        "fingerprint": "sample-attack.tenant_read",
        "ruleId": "attack.tenant_read",
        "title": "One user can read another user's records by changing the id",
        "why": "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.",
        "urgency": "fix_now",
        "urgencyLabel": "Fix now",
        "cvss": {
          "version": "4.0",
          "score": 7.1,
          "rating": "High",
          "vector": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N",
          "typical": false
        },
        "cwe": [
          "CWE-639"
        ],
        "owasp": [
          "OWASP A01:2021 Broken Access Control",
          "OWASP API1:2023 Broken Object Level Authorization"
        ],
        "evidence": "200 with another customer's order",
        "place": "GET /api/orders/{id} (as user b)",
        "reproduction": [
          "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."
        ],
        "impact": "People could see or change data that belongs to someone else.",
        "expected": "An account can open only its own records; another account's id answers 404 or 403.",
        "fix": {
          "text": "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.",
          "code": null
        },
        "references": [
          "CWE-639: Authorization Bypass Through User-Controlled Key (https://cwe.mitre.org/data/definitions/639.html)",
          "OWASP A01:2021: Broken Access Control",
          "OWASP API1:2023: Broken Object Level Authorization"
        ],
        "status": "open",
        "statusLabel": "Open",
        "acceptedReason": null,
        "acceptedBy": null,
        "acceptedAt": null,
        "firstSeen": "2026-10-30",
        "lastSeen": "2026-11-07",
        "fixedAt": null,
        "source": "attack",
        "verdict": "confirmed",
        "severity": "high",
        "verification": {
          "basis": "observed",
          "reasons": [
            "observed_live"
          ],
          "why": [
            "yof saw it on the live app."
          ]
        },
        "at": {
          "file": null,
          "line": null,
          "url": "https://shop.example/api/orders/{id}"
        }
      },
      {
        "ref": "F-005",
        "fingerprint": "sample-email.dmarc_missing",
        "ruleId": "email.dmarc_missing",
        "title": "Your domain has no rule for fake email",
        "why": "Mail providers don't know what to do with messages that pretend to be you, so many of them get delivered.",
        "urgency": "fix_week",
        "urgencyLabel": "Fix this week",
        "cvss": {
          "version": "4.0",
          "score": 5.1,
          "rating": "Medium",
          "vector": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:A/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N",
          "typical": false
        },
        "cwe": [
          "CWE-290"
        ],
        "owasp": [
          "OWASP A05:2021 Security Misconfiguration"
        ],
        "evidence": null,
        "place": "https://shop.example/",
        "reproduction": [
          "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."
        ],
        "impact": "A setting leaves an opening that is easy to find.",
        "expected": "The domain publishes a DMARC policy of quarantine or reject.",
        "fix": {
          "text": "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.",
          "code": {
            "lang": "dns",
            "body": "_dmarc.shop.example.  TXT  \"v=DMARC1; p=quarantine; rua=mailto:dmarc@shop.example\""
          }
        },
        "references": [
          "CWE-290: Authentication Bypass by Spoofing (https://cwe.mitre.org/data/definitions/290.html)",
          "OWASP A05:2021: Security Misconfiguration"
        ],
        "status": "fixed",
        "statusLabel": "Fixed",
        "acceptedReason": null,
        "acceptedBy": null,
        "acceptedAt": null,
        "firstSeen": "2026-10-30",
        "lastSeen": "2026-11-07",
        "fixedAt": "2026-11-04",
        "source": "outside",
        "verdict": "needs_validation",
        "severity": "medium",
        "verification": {
          "basis": "observed",
          "reasons": [
            "evidence_missing"
          ],
          "why": [
            "The check did not keep the evidence needed to confirm it."
          ]
        },
        "at": {
          "file": null,
          "line": null,
          "url": "https://shop.example/"
        }
      }
    ],
    "hardening": [
      {
        "ref": "H-001",
        "fingerprint": "sample-headers.hsts",
        "ruleId": "headers.hsts",
        "title": "Browsers aren't told to always use https",
        "why": "Without this setting, a visitor's first visit can still be pushed onto an unencrypted connection.",
        "urgency": "nice",
        "urgencyLabel": "Nice to have",
        "cvss": {
          "version": "4.0",
          "score": 2.1,
          "rating": "Low",
          "vector": "CVSS:4.0/AV:A/AC:H/AT:P/PR:N/UI:P/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N",
          "typical": false
        },
        "cwe": [
          "CWE-319"
        ],
        "owasp": [
          "OWASP A05:2021 Security Misconfiguration"
        ],
        "evidence": null,
        "place": "https://shop.example/",
        "reproduction": [
          "Request https://shop.example/ and read the response headers (for example: curl -sI <address>).",
          "Expected instead: The site sends Strict-Transport-Security with max-age of at least one year."
        ],
        "impact": "A setting leaves an opening that is easy to find.",
        "expected": "The site sends Strict-Transport-Security with max-age of at least one year.",
        "fix": {
          "text": "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.",
          "code": {
            "lang": "json",
            "body": "// vercel.json (or the same header in your host's settings)\n{\n  \"headers\": [\n    { \"source\": \"/(.*)\", \"headers\": [ { \"key\": \"Strict-Transport-Security\", \"value\": \"max-age=63072000; includeSubDomains\" } ] }\n  ]\n}"
          }
        },
        "references": [
          "CWE-319: Cleartext Transmission of Sensitive Information (https://cwe.mitre.org/data/definitions/319.html)",
          "OWASP A05:2021: Security Misconfiguration"
        ],
        "status": "open",
        "statusLabel": "Open",
        "acceptedReason": null,
        "acceptedBy": null,
        "acceptedAt": null,
        "firstSeen": "2026-10-30",
        "lastSeen": "2026-11-07",
        "fixedAt": null,
        "source": "outside",
        "verdict": null,
        "severity": "info",
        "verification": {
          "basis": "practice",
          "reasons": [
            "hardening"
          ],
          "why": [
            "Good practice; no risk was demonstrated."
          ]
        },
        "at": {
          "file": null,
          "line": null,
          "url": "https://shop.example/"
        }
      },
      {
        "ref": "H-002",
        "fingerprint": "sample-headers.csp",
        "ruleId": "headers.csp",
        "title": "No rule limits which scripts can run",
        "why": "If someone manages to slip code into a page, nothing stops the browser from running it.",
        "urgency": "fix_week",
        "urgencyLabel": "Fix this week",
        "cvss": {
          "version": "4.0",
          "score": 2.1,
          "rating": "Low",
          "vector": "CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:A/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N",
          "typical": false
        },
        "cwe": [
          "CWE-693"
        ],
        "owasp": [
          "OWASP A05:2021 Security Misconfiguration"
        ],
        "evidence": null,
        "place": "https://shop.example/",
        "reproduction": [
          "Request https://shop.example/ and read the response headers (for example: curl -sI <address>).",
          "Expected instead: The site sends a Content-Security-Policy that blocks inline and outside scripts."
        ],
        "impact": "A setting leaves an opening that is easy to find.",
        "expected": "The site sends a Content-Security-Policy that blocks inline and outside scripts.",
        "fix": {
          "text": "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.",
          "code": {
            "lang": "json",
            "body": "// vercel.json (or the same header in your host's settings)\n{\n  \"headers\": [\n    { \"source\": \"/(.*)\", \"headers\": [ { \"key\": \"Content-Security-Policy\", \"value\": \"default-src 'self'; frame-ancestors 'self'; object-src 'none'; base-uri 'self'\" } ] }\n  ]\n}"
          }
        },
        "references": [
          "CWE-693: Protection Mechanism Failure (https://cwe.mitre.org/data/definitions/693.html)",
          "OWASP A05:2021: Security Misconfiguration"
        ],
        "status": "accepted",
        "statusLabel": "Accepted risk",
        "acceptedReason": "The payment widget needs inline scripts until its next version; we review this in January.",
        "acceptedBy": "owner@shop.example",
        "acceptedAt": "2026-11-05",
        "firstSeen": "2026-10-30",
        "lastSeen": "2026-11-07",
        "fixedAt": null,
        "source": "outside",
        "verdict": null,
        "severity": "info",
        "verification": {
          "basis": "practice",
          "reasons": [
            "hardening"
          ],
          "why": [
            "Good practice; no risk was demonstrated."
          ]
        },
        "at": {
          "file": null,
          "line": null,
          "url": "https://shop.example/"
        }
      }
    ],
    "rejected": [],
    "verification": {
      "heading": "How sure yof is of each finding",
      "intro": "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.",
      "verdicts": [
        {
          "id": "confirmed",
          "label": "Confirmed",
          "meaning": "yof saw the impact happen, or holds the exact evidence (for example the key that was served, or the installed version an advisory names)."
        },
        {
          "id": "needs_validation",
          "label": "Needs validation",
          "meaning": "Likely, but not demonstrated: yof saw the pattern and could not prove the impact. A person or a deeper test needs to confirm it."
        },
        {
          "id": "rejected",
          "label": "Rejected",
          "meaning": "Disproved by a second, separate check. Kept for the record, not counted."
        }
      ],
      "anchorsHeading": "What each severity means",
      "anchors": [
        {
          "level": "critical",
          "label": "Critical",
          "anchor": "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."
        },
        {
          "level": "high",
          "label": "High",
          "anchor": "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."
        },
        {
          "level": "medium",
          "label": "Medium",
          "anchor": "Makes an attack easier or its damage larger, under conditions such as a sign-in, a user's click or a specific setting."
        },
        {
          "level": "low",
          "label": "Low",
          "anchor": "Small harm on its own, or reachable only in development and test tools."
        },
        {
          "level": "info",
          "label": "Info",
          "anchor": "Worth knowing, no harm on its own."
        }
      ],
      "counts": {
        "confirmed": 4,
        "needs_validation": 0,
        "hardening": 1,
        "rejected": 0
      },
      "countsLine": "Open findings only, by verdict: confirmed (4), needs validation (0). Listed separately: hardening notes (1), rejected (0).",
      "hardeningHeading": "Hardening notes (not counted as findings)",
      "hardeningIntro": "Good practice that would add a layer of protection. yof demonstrated no risk, so these are not findings and are in no count.",
      "hardeningNone": "No hardening notes.",
      "rejectedHeading": "Rejected (kept for the record, not counted)",
      "rejectedIntro": "Findings a second, separate check disproved. They are listed so the same claim is not raised again without new evidence."
    }
  },
  "ai": {
    "heading": "AI security and accuracy",
    "intro": "What yof tested in the AI parts of this app, by the OWASP Top 10 for LLM Applications 2025, and how often the assistant's answers matched the owner's own pages. Probes are fixed and mild, sent only with the owner's consent; answers are read by fixed rules, never by another model.",
    "wording": "Evidence for the technique, function or article theme named. It does not say any of them is met, and it does not say the EU AI Act applies to this app; a person decides that.",
    "tested": {
      "assistant": false,
      "attack": true,
      "code": true
    },
    "probes": [],
    "llm": [
      {
        "id": "LLM01",
        "name": "Prompt Injection",
        "state": "Tested: nothing open",
        "refs": []
      },
      {
        "id": "LLM02",
        "name": "Sensitive Information Disclosure",
        "state": "Tested: nothing open",
        "refs": []
      },
      {
        "id": "LLM03",
        "name": "Supply Chain",
        "state": "Tested: nothing open",
        "refs": []
      },
      {
        "id": "LLM04",
        "name": "Data and Model Poisoning",
        "state": "Tested: nothing open",
        "refs": []
      },
      {
        "id": "LLM05",
        "name": "Improper Output Handling",
        "state": "Tested: nothing open",
        "refs": []
      },
      {
        "id": "LLM06",
        "name": "Excessive Agency",
        "state": "Tested: nothing open",
        "refs": []
      },
      {
        "id": "LLM07",
        "name": "System Prompt Leakage",
        "state": "Tested: nothing open",
        "refs": []
      },
      {
        "id": "LLM08",
        "name": "Vector and Embedding Weaknesses",
        "state": "Not tested by yof",
        "refs": []
      },
      {
        "id": "LLM09",
        "name": "Misinformation",
        "state": "Not tested by yof",
        "refs": []
      },
      {
        "id": "LLM10",
        "name": "Unbounded Consumption",
        "state": "Tested: nothing open",
        "refs": []
      }
    ],
    "accuracy": null,
    "coding": [],
    "mapping": [],
    "notTested": [
      "The assistant was not tested in this run: no setup with the owner's consent.",
      "Vector store and embedding weaknesses (OWASP LLM08) are not tested by yof."
    ]
  },
  "compliance": null,
  "cra": null,
  "dependencies": {
    "heading": "Dependencies and SBOM",
    "repo": "example-shop/storefront",
    "commit": "0f1e2d3c4b5a69788796a5b4c3d2e1f00f1e2d3c",
    "components": 640,
    "ecosystems": {
      "npm": 640
    },
    "lockfiles": [
      "package-lock.json"
    ],
    "vulnerable": 1,
    "sbomSha256": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a",
    "capped": false,
    "vulnerablePackages": [
      {
        "ref": "F-001",
        "title": "minimist 1.2.5 has a known serious vulnerability (package-lock.json)",
        "place": "package-lock.json:812 (example-shop/storefront at 0f1e2d3c4b5a)",
        "package": {
          "name": "minimist",
          "ecosystem": "npm",
          "installed": "1.2.5",
          "advisories": [
            {
              "id": "GHSA-xvch-5gv4-984h",
              "url": "https://github.com/advisories/GHSA-xvch-5gv4-984h"
            },
            {
              "id": "CVE-2021-44906",
              "url": "https://nvd.nist.gov/vuln/detail/CVE-2021-44906"
            }
          ],
          "fixed": "1.2.6",
          "lockfile": "package-lock.json:812",
          "lockfiles": [],
          "manifests": [
            "package.json"
          ],
          "purl": "pkg:npm/minimist@1.2.5"
        }
      }
    ],
    "note": "The full list of components is in the CycloneDX 1.5 file that comes with this report (SBOM)."
  },
  "retest": null,
  "accepted": {
    "heading": "Accepted risks and why",
    "intro": "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.",
    "none": "No risks were accepted for this report.",
    "items": [
      {
        "ref": "H-002",
        "ruleId": "headers.csp",
        "title": "No rule limits which scripts can run",
        "place": "https://shop.example/",
        "reason": "The payment widget needs inline scripts until its next version; we review this in January.",
        "by": "owner@shop.example",
        "at": "2026-11-05",
        "source": "app"
      },
      {
        "ref": null,
        "ruleId": "secret.stripe_test",
        "title": "A Stripe test secret key is in your page code",
        "place": "tests/fixtures/payments.ts",
        "reason": "A Stripe test key in a test fixture; it cannot take real payments.",
        "by": "Sam (developer)",
        "at": "2026-11-02",
        "source": "repo"
      }
    ]
  },
  "appendix": {
    "heading": "Appendix: every check run",
    "checks": [
      {
        "area": "Keys in page code",
        "status": "Ran in full",
        "things": 14
      },
      {
        "area": "Database access",
        "status": "Ran in full",
        "things": 4
      },
      {
        "area": "Files that should be private",
        "status": "Ran in full",
        "things": 42
      },
      {
        "area": "Admin pages",
        "status": "Ran in full",
        "things": 9
      },
      {
        "area": "Public API",
        "status": "Ran in part",
        "things": 6
      },
      {
        "area": "Browser protection headers",
        "status": "Ran in full",
        "things": 1
      },
      {
        "area": "Email sending rules",
        "status": "Ran in full",
        "things": 2
      },
      {
        "area": "Website in a real browser",
        "status": "Ran in full",
        "things": 1
      },
      {
        "area": "Packages",
        "status": "Ran in full",
        "things": 640
      },
      {
        "area": "Keys in the code",
        "status": "Ran in full",
        "things": 214
      },
      {
        "area": "Configuration",
        "status": "Ran in full",
        "things": 214
      },
      {
        "area": "Code",
        "status": "Ran in full",
        "things": 170
      },
      {
        "area": "Attack tests",
        "status": "Ran in full",
        "things": 1
      }
    ],
    "proof": [
      "bundle | script | https://shop.example/assets/app.js | found something",
      "headers | header | https://shop.example/ | ok",
      "files | address | https://shop.example/.env (x42) | ok",
      "database | table | orders | found something"
    ],
    "proofNote": "One line for each thing yof looked at in the outside run, and the result (lines: 4).",
    "otherNotes": []
  },
  "files": {
    "heading": "Machine files and how to check this report",
    "kinds": [
      "pdf",
      "json",
      "sarif",
      "findings_json",
      "xlsx",
      "jira_csv",
      "linear_csv",
      "cyclonedx"
    ],
    "verify": [
      "Report ID: 5a3b1e00-0000-4000-8000-000000000000. Version: 1. Issued: 2026-11-07T09:00:00.000Z.",
      "The JSON file holds this whole report; its content hash is the SHA-256 of the canonical JSON without the signature block.",
      "Check a copy against yof's public register: GET https://api.mondaily.com/api/v1/public/yof-reports/verify?id=<report id>&sha=<hash>. The hash of the PDF or any other file of this report is accepted too.",
      "Or scan the QR code on the cover, or open https://yof.io/verify/5a3b1e00-0000-4000-8000-000000000000: the page checks the signature and shows only what the report's owner chose to share."
    ],
    "verifyUrl": "https://yof.io/verify/5a3b1e00-0000-4000-8000-000000000000"
  },
  "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.",
    "Coverage was 45%. Parts that were not tested are listed in section 3."
  ],
  "signature": {
    "alg": "none (sample, not signed)",
    "value": "Sample report: not signed and not in yof's register.",
    "sha256": "fb9ff4c2ff530d7b8b05f4622d7e389ab732e25f51676dc2cd21e1f34878fee5",
    "scheme": "yof-report/v1 sample"
  }
}
