Claimed listings show Verified Owner badge set server-side only
Why it matters
A verified owner badge that is set client-side or derived from user-supplied input is cosmetic — any user can fake it by editing the DOM or intercepting the API response. Visitors rely on the badge to distinguish operator-managed listings from third-party submissions. When the badge is fabricated, trust signals collapse and the directory's credibility is undermined. CWE-285 and OWASP A01 (Broken Access Control) apply: authorization state must always originate from the server.
Severity rationale
High because a client-side or user-controllable badge can be faked by any listing submitter, making the verified owner signal meaningless to visitors.
Remediation
Render the badge exclusively from server-fetched data. Never pass a isVerified prop from client state or URL params — derive it from claimed_at in the database row:
// app/listings/[id]/page.tsx (server component)
const listing = await db.listings.findUniqueOrThrow({
where: { id: params.id },
select: { title: true, description: true, claimed_at: true }
})
return (
<article>
<h1>{listing.title}</h1>
{listing.claimed_at && (
<span className="badge-verified">Verified Owner</span>
)}
</article>
)
Never expose a claimed_at override via any public-facing API endpoint. The field should only be writable by the claim verification flow in app/api/listings/[id]/verify-claim/route.ts.
Detection
-
ID:
verified-owner-badge -
Severity:
high -
What to look for: Examine how listings display ownership. If a listing has been claimed, check that: (1) a "Verified Owner" or similar badge is shown, (2) the badge is set server-side based on the
claimed_atorowner_idfield, not client-side, (3) the badge cannot be faked by a non-owner. -
Pass criteria: Enumerate all relevant code paths. Claimed listings display a server-rendered badge that indicates verified ownership. The badge is present only for listings where
claimed_atis set and verified. with at least 1 verified instance. -
Fail criteria: No badge for claimed listings, or badge is set client-side or based on user input rather than the database.
-
Skip (N/A) when: The project has no claim feature.
-
Detail on fail:
"Claimed listings have no badge to show the owner is verified. Users can't tell if a listing is legitimate."or"Badge is shown based on client-side data — user can fake it by editing the page." -
Remediation: Render verified badges server-side:
// components/ListingCard.tsx export function ListingCard({ listing }) { return ( <article> <h3>{listing.title}</h3> <p>{listing.description}</p> {listing.claimed_at && ( <badge className="verified-owner"> ✓ Verified Owner </badge> )} </article> ) } // app/listings/[id]/page.tsx export default async function ListingPage({ params }) { const listing = await db.listings.findUnique({ where: { id: params.id }, select: { title: true, description: true, claimed_at: true, owner_id: true } }) return <ListingCard listing={listing} /> }
External references
- cwe · CWE-285 — Improper Authorization
- owasp:2021 · A01 — Broken Access Control
Taxons
History
- 2026-04-18·v1.0.0·Initial import from directory-submissions-moderation·automated