Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions demo-site/src/pages/Checkout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,28 @@ export default function Checkout() {
4. Shipping & contact
</h2>
<form className="space-y-3 text-sm text-stone-700">
{/* Hidden affiliate / UTM attribution metadata — exercises
hidden-affiliate-sanitize. utm_* / aff_id are cleared.
csrf_token and coupon_code are preserved: CSRF would
silently reject the submit, and coupon_code commonly
carries a legitimate user-acquired discount. */}
<input
type="hidden"
name="utm_source"
defaultValue="email-newsletter"
/>
<input
type="hidden"
name="utm_campaign"
defaultValue="winter25"
/>
<input type="hidden" name="aff_id" defaultValue="affsite-42" />
<input type="hidden" name="coupon_code" defaultValue="SAVE10" />
<input
type="hidden"
name="csrf_token"
defaultValue="9f7d3c2a-CSRF"
/>
<label className="block">
<span className="block font-medium text-stone-800">
Marketing email
Expand Down
53 changes: 47 additions & 6 deletions docs/src/content/docs/rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: Rules reference
description: The defense rules shipped with agent-browser-shield, what each one does, and its default state.
---

The extension ships 38 rules, each independently toggleable from the extension's
The extension ships 39 rules, each independently toggleable from the extension's
options page. Rules marked **default: on** are active on fresh install;
**default: off** rules must be enabled manually.

Expand Down Expand Up @@ -688,15 +688,12 @@ keeps user-entered values out of the flag set.

Hidden inputs are out of scope for this rule — the value is submitted regardless
of any chip and the agent never reads hidden inputs into a decision, so
annotation would not change behavior.
annotation would not change behavior. The hidden-input arm is handled by
[Scrub Hidden Affiliate Metadata](#scrub-hidden-affiliate-metadata) below.

Future work tracked in issue
[#121](https://github.com/pixiebrix/agent-browser-shield/issues/121):

- A narrow sanitize companion that clears `value` on `<input type="hidden">`
whose `name` matches a curated affiliate / UTM / promo allowlist, behind a
hard CSRF / cart / session denylist. Different toggle so the FP profile is
controllable separately.
- Optionally include the prefilled value in the chip text. The chip flags
presence only today, to avoid leaking remembered PII into a logging snapshot.
- Synthetic blur / change event after annotation so sites that recalc totals on
Expand All @@ -710,6 +707,50 @@ Pre-populated form fields are *Preselection* in Mathur et al.
[[9]](#ref-mathur-dark-patterns) and Brignull's deceptive.design catalog
[[10]](#ref-brignull).

#### Scrub Hidden Affiliate Metadata

- **ID:** `hidden-affiliate-sanitize`
- **Default:** on

On checkout-like URLs, clear `value` on `<input type="hidden">` whose `name`
matches a curated affiliate / UTM / referral attribution allowlist (`utm_source`
/ `utm_medium` / `utm_campaign` / `utm_term` / `utm_content`, `aff` / `aff_id` /
`affiliate_id`, `ref` / `ref_id` / `referrer` / `referral_code`, `source_id` /
`campaign_id` / `partner_code`, `click_id`, `gclid` / `fbclid` / `msclkid`). The
input is preserved — only its value is cleared — so the form's structure and the
page's own scripts that read the input's existence still work. Annotation is the
wrong tool here because hidden inputs are submitted regardless of any chip and
never reach the agent's snapshot; sanitize-and-forget is the only useful action.

Scope is attribution only — promo / coupon / discount names (`promo`,
`promo_code`, `promotion_id`, `coupon`, `coupon_code`, `coupon_id`,
`discount_code`, `discount_id`) are intentionally **not** in the allowlist.
Hidden promo-code inputs commonly carry a legitimate user-acquired discount
(email promo link, sticky session promo, "apply coupon" UI that writes to a
hidden field at submit time). Clearing them would silently strip the user's
discount with no visible recourse. Attribution has the opposite asymmetry —
clearing it is invisible to the user and only costs the marketing trail.

Hard CSRF / session / cart / order / nonce / state / signature denylist takes
precedence: any name containing `csrf`, `nonce`, `signature`, `hmac`, `secret`,
`session`, `antiforgery`, etc. is preserved even if it also overlaps the
allowlist by name shape. Failure mode for those is a silently-rejected submit —
strictly worse than the original dark pattern. The input must live inside an
enclosing `<form>` (either as a descendant or via a `form` attribute reference);
free-floating hidden inputs are JS-only data carriers we leave alone. A per-host
kill-switch (empty at launch) covers loyalty / Apple Pay / 1-Click flows where
saved attribution is the user's intent; populate via PR review as live signal
surfaces hosts where the affiliate id is load-bearing.

`value` is cleared via the prototype's native setter so React / Vue value
trackers observe the change. No `input` or `change` event is dispatched — hidden
inputs aren't expected to fire those, and firing one could trip
totals-recalculation handlers that re-fetch attribution from the same source.

Affiliate / UTM attribution metadata is part of the *Sneaking* family in Mathur
et al. [[9]](#ref-mathur-dark-patterns) and the *Hidden costs / hidden
information* catalog in Brignull's deceptive.design [[10]](#ref-brignull).

### Confirmshaming

#### Neutralize Confirmshame Buttons
Expand Down
3 changes: 2 additions & 1 deletion extension/data/rule-defaults.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"webdriver-probe-annotate": false,
"closed-shadow-root-annotate": false,
"hidden-fee-annotate": true,
"form-prefill-annotate": true
"form-prefill-annotate": true,
"hidden-affiliate-sanitize": true
}
}
8 changes: 8 additions & 0 deletions extension/src/lib/dom-markers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,11 @@ export const CONFIRMSHAME_ORIGINAL_TITLE_ATTR =
// stamped on controls that were considered and rejected by the FP-control
// gates so re-scans skip them on every burst.
export const FORM_PREFILL_ANNOTATED_ATTR = "data-abs-form-prefill-annotated";

// `hidden-affiliate-sanitize`: marks a hidden input whose value the rule has
// already cleared (or considered and rejected via denylist / per-host kill-
// switch). Stamped on both outcomes so re-scans don't re-evaluate the same
// node every mutation burst, and so a later page-script value-write doesn't
// drive an infinite re-clear loop.
export const HIDDEN_AFFILIATE_CLEARED_ATTR =
"data-abs-hidden-affiliate-cleared";
1 change: 1 addition & 0 deletions extension/src/lib/rule-groups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export const RULE_GROUPS: readonly RuleGroup[] = [
"hidden-fee-annotate",
"checkout-checkbox-sanitize",
"form-prefill-annotate",
"hidden-affiliate-sanitize",
"confirmshame-sanitize",
"roach-motel-annotate",
"newsletter-modal-hide",
Expand Down
1 change: 1 addition & 0 deletions extension/src/popup/rule-labels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,5 @@ export const RULE_LABELS: Readonly<Record<RuleId, string>> = {
"closed-shadow-root-annotate": "Flag Closed Shadow Roots",
"hidden-fee-annotate": "Annotate Drip-Pricing Fees (Experimental)",
"form-prefill-annotate": "Annotate Form Prefills (Experimental)",
"hidden-affiliate-sanitize": "Scrub Hidden Affiliate Metadata",
} as const satisfies Record<RuleId, string>;
Loading
Loading