Report No. SIL-2026-0613 · Filed 06·19·2026
Subject of Audit
harborandvine.com
ownership attested ✓
owner private view
active probe enabled
Verdict: Not ready for client handoff
Why it can't ship yet
One blocking issue: a private database key is exposed in the site's code, which hands anyone who opens the page full read and write access to your data. Security also sits below our release floor.
Readiness Index
the index is diagnostic. the gate verdict above is what governs release.
Accessibility
WCAG 2.2 AA 72/100Above the release floor, but three deterministic failures would each be visible to a client using a keyboard or screen reader. axe-core ran against the rendered DOM; judgment checks are marked for human review rather than auto-failed.
Reserve-a-table button fails contrast
The gold button text on cream measures 2.1:1. AA requires 4.5:1 for this size. A client with low vision can't read the primary call to action.
▸ evidence
foreground #C9A24B
background #F5F0E6
ratio 2.1:1 (needs 4.5:1)
wcag 1.4.3 Contrast (Minimum) · AA
▸ fix prompt · suggestion-grade
Reservation form inputs have no labels
Three inputs rely on placeholder text alone. A screen reader announces them as unlabeled, and the placeholder vanishes once typing starts.
▸ evidence
#reservation input[name=email] no <label>, no aria-label
#reservation input[name=party] no <label>, no aria-label
wcag 1.3.1 Info & Relationships · 4.1.2 Name, Role, Value
Gallery images missing alt text
Three of five gallery images carry no alt attribute, so a screen reader reads the file path or nothing at all.
▸ evidence
img /gallery/pour-3.jpg (no alt attribute)
img /gallery/cellar-5.jpg (no alt attribute)
wcag 1.1.1 Non-text Content · A
Mobile menu focus order needs a human check
The menu opens in a portal. Tab order looks plausible but logical focus order is a judgment call, so we flag it for review rather than asserting a pass or a fail. We never claim a WCAG pass we did not verify.
Security
floor 60 · breached 24/100This dimension holds the verdict. One verified critical and a missing header stack put it well below the release floor. The two-stage secret detector cleared the public keys before flagging the dangerous one, which is the line that separates a real finding from a false alarm.
Supabase service_role key exposed in the page bundle
The service_role key bypasses every row-level-security rule. Anyone who opens the site can read, edit, and delete your entire database from their browser. This is the single issue that blocks handoff. Treat the key as compromised and rotate it now, before fixing the code.
▸ evidence · redacted
location line 1 · col 88204
assigned const SUPABASE_KEY = "…"
decoded jwt payload → "role":"service_role"
value eyJhbGciOiJI•••••••••••• redacted ••••••••••••9.Qx7vK
▸ fix prompt · suggestion-grade
Stripe publishable key, public by design, not a leak
A pk_live_ key sits in the checkout script. Publishable keys are meant to be public, so we cleared it against the allowlist instead of flagging it. Telling this apart from a real leak is the difference between a report a client trusts and one that cries wolf.
▸ why this is safe
matched allowlist: stripe publishable
action suppressed before stage 2. never escalated.
note also cleared: supabase anon jwt (role:anon)
No security headers set
The response ships with none of the baseline protections. Graded against the Observatory algorithm it lands at F.
▸ evidence
absent strict-transport-security
absent x-content-type-options
absent referrer-policy
absent permissions-policy
grade F (start 100, penalties applied)
Source maps are publicly reachable
A reachable .js.map exposes your original source, which is how the key above was so easy to read. Disable source-map output for production builds.
▸ evidence
exposes original src tree, component names, comments
Performance & Core Web Vitals
source: lighthouse / psi / crux 58/100Presented as diagnostic. Lighthouse is lab data and does not measure field CWV directly, so no single number drives the verdict. Field data from CrUX is not yet available for this domain, which we flag rather than guess.
Hero image is a 2.3 MB unoptimized PNG
The largest contentful paint is the hero photo. It ships uncompressed with no dimensions set, which drives both the slow LCP and most of the layout shift.
▸ evidence
served no width/height attrs, no srcset, no preload
impact lcp element · ~2.1s of the 4.1s
SEO & Structured Data
66/100The basics are present but the page is invisible to link previews and to local search. For a wine bar, the missing LocalBusiness schema is a real miss.
No meta description · no canonical
Search engines write their own snippet, and duplicate URLs can't be consolidated.
No Open Graph or Twitter cards
Shared to social or messaging, the link shows a blank box with no title or image.
No LocalBusiness schema
Hours, address, and price range aren't machine-readable, so the map and rich results stay empty.
AI-tell / Slop
E1 convicts · E2 advisory 38/100The leftovers of an AI build that shipped before anyone read it top to bottom. E1 tells are facts and score at full weight. E2 tells are judgment, kept advisory, and can never decide a verdict.
E1 · deterministic tells
× fail placeholder image · .menu img src=via.placeholder.com/640x400
× fail dummy email in footer · hello@example.com
× fail dead nav link · a[href="/events"] → 404
× fail console error on load · TypeError: Cannot read properties of null (reading 'map')
· ctx generator fingerprint · lovable (cdn.gpteng.co/gptengineer.js)
▸ fix prompt · lorem ipsum · suggestion-grade
E2 · inferred · advisory only, cannot convict
Hero copy "Welcome to our amazing restaurant" reads templated. This feeds the Polish signal only. It does not affect the verdict.
"Our Story" repeats the same three-card boilerplate seen in many generated builds. Advisory, not a fault.
Functional Integrity
44/100The page renders, but its two main jobs, taking a reservation and an online order, both do nothing when clicked. Detected from the captured console and network logs.
Reservation form submits nowhere
The form's onSubmit handler is missing. The button shows a success message but no request leaves the browser, so every reservation is silently lost. This is the kind of finding a client discovers by losing a booking.
▸ evidence
network 0 requests fired on submit
ui shows "Thanks, see you soon" with no post
▸ fix prompt · suggestion-grade
"Order online" button has no handler
Clicking does nothing. No navigation, no modal, no console activity.
Two gallery images return 404
/gallery/pour-3.jpg and /gallery/cellar-5.jpg fail to load, leaving broken-image icons.
Privacy & Legal Hygiene
informational 70/100Flagged for awareness, not scored against the gate. This is information, not legal advice.
Google Analytics loads on entry, but no consent banner was detected. Depending on visitor location this may need addressing.
No privacy-policy link in the footer or nav.
Responsive / Mobile
63/100Most clients open the link on a phone first. Two issues show up at 375px wide.
Horizontal overflow at 375px
The menu table is 40px wider than the viewport, so the whole page scrolls sideways.
Footer social icons are 28px tap targets
Below the 44px minimum, hard to tap accurately on a phone.