Fix: schema-trust Person annotation + broader disguised-ad coverage (#203)#209
Conversation
…203) Bundles three audit items from #203: - #15 schema-trust-sanitize: Person nested under an authority-context property (author / editor / publisher / reviewedBy / etc.) with a cross-RD URL is now annotated rather than ignored. JSON-LD nodes get `abs:unverified-authority: true`; microdata scopes get `data-abs-schema-trust-unverified="true"`. Identity is preserved because sanitizing would erase legitimate guest-author and academic bylines that routinely link off-domain. Standalone Person scopes (personal homepages) are left alone — they aren't borrowing anyone's authority. - #18 disguised-ad-flag: extended label vocabulary with "Featured Listing", "From our Advertisers", "Marketing Partner", suffix-form "In partnership with <Brand>", and lowercase `[ad]`. The lowercase bracket form is safe because the anchored regex rejects internal whitespace, so editorial idioms like `[ad hoc]` / `[ad lib]` can't match. - #19 disguised-ad-flag: heading-equivalent selector now accepts `[role="heading"]` (ARIA spec) and `[class~="headline"]` (exact-token match) alongside h1-h6, so design systems that emit a styled `<div class="headline">` instead of a real `<h*>` still get caught. Selector is narrow — broader title/heading class matches would pull in chrome like `.page-title` / `.video-title`. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
1 issue found.
About Unblocked
Unblocked has been set up to automatically review your team's pull requests to identify genuine bugs and issues.
📖 Documentation — Learn more in our docs.
💬 Ask questions — Mention @unblocked to request a review or summary, or ask follow-up questions.
👍 Give feedback — React to comments with 👍 or 👎 to help us improve.
⚙️ Customize — Adjust settings in your preferences.
Stale comment: "case-sensitive on Ad" no longer accurate
agent-browser-shield/extension/src/rules/disguised-ad-flag.ts
Lines 125 to 127 in a580c2d
The comment says "Bracket form is case-sensitive on Ad" but BRACKET_RE now also matches lowercase [ad]. The comment should be updated to reflect that BRACKET_RE is case-sensitive (no i flag) but intentionally matches three case variants ([ad], [Ad], [AD]).
function matchLabel(text: string): LabelMatch | null {
// Bracket form has no `i` flag (case-sensitive), so check it before the
// case-insensitive regexes.
if (BRACKET_RE.test(text)) {
Update stale comment in matchLabel — after adding lowercase `[ad]`, the comment claimed "case-sensitive on `Ad`" which was no longer accurate. Rewrite to describe the actual safety property (anchored regex rejects internal whitespace) so the editorial-idiom argument that motivates the enumeration is visible to the next reader. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
|
@unblocked — good catch, the "case-sensitive on |
Summary
Bundles three audit items from #203 (
schema-trust#15,disguised-ad-flag#18 + #19).schema-trust-sanitize. Sanitize-on-Person is too noisy to ship (legit guest-author and academic bylines routinely link off-domain), but the same shape is the exact carrier for byline impersonation (Article.author = Person{name:"Sanjay Gupta", url:"cnn.com"}). Resolution: when aPersonis nested under an authority-context property (author/editor/publisher/reviewedBy/ etc.) and itsurlis cross-RD, stamp the JSON-LD node withabs:unverified-authority: trueand the microdata scope withdata-abs-schema-trust-unverified. Identity (name/url/@id) preserved. Standalone@type: Personleft alone — not borrowing anyone's authority.disguised-ad-flag. Adds standalonesFeatured Listing,From our Advertisers,Marketing Partner; suffix-formIn partnership with <Brand>; and lowercase[ad]. The lowercase bracket form is safe because the regex is anchored on trimmed text with no internal whitespace — editorial idioms ([ad hoc],[ad lib]) can't match.HEADING_SELECTORaccepts[role="heading"](ARIA spec) and[class~="headline"](exact-token match) alongsideh1–h6. Used in both the article-shape gate and the nested-prose-skip gate. Selector is narrow — broader*title*/*heading*matches would pull in chrome (.page-title,.video-title).Why-comments added inline at each decision so future readers see the FP analysis (Person) and the safety argument for lowercase
[ad]without re-deriving them.Test plan
bun run check— biome + eslint cleanbun run typecheck— cleanbun run knip— cleanschema-trust+disguised-ad-flagsuites (lib + rule + skip + property)pre-commit run --files docs/src/content/docs/rules.md— mdformat + markdownlint cleanArticle.author = Person{...cross-RD...}on the demo site and confirm the rule annotates without breaking the byline; verify a standalone@type: Personpage is untouched🤖 Generated with Claude Code