Skip to content

Add new scriptlets — 'edit-object-on-getter' and 'edit-object-on-setter' #568

Description

@slvvko

uBlock Origin recently added four new scriptlets that AdGuard has no equivalent for — (commit 99e6228):

  • edit-object-on-getter.js — prune properties from an object on read access
  • trusted-edit-object-on-getter.js — edit (set new values) properties of an object on read access
  • edit-object-on-setter.js — prune properties from an object on write access
  • trusted-edit-object-on-setter.js — edit (set new values) properties of an object on write access

How they work

Scriptlet intercepts property descriptor:
  propChain (e.g. "window.__data")
       ↓
  getter trap OR setter trap
       ↓
  JSONPath query applied (prune / edit)
       ↓
  Modified object returned to / stored for page scripts

The core mechanism uses trapProperty that walks the property chain, replaces the descriptor with { get, set } traps, and on access applies JSONPath edits.

Gap analysis

AdGuard scriptlet What it does Why it's not equivalent
set-constant / trusted-set-constant Replaces entire property value No selective editing — can't say "remove trackingId but keep userName"
json-prune / json-prune-fetch-response / json-prune-xhr-response Intercepts JSON.parse / fetch / XHR Only works on network/parse boundaries, not arbitrary property access
trusted-json-set Intercepts method calls via Proxy Intercepts function calls, not property descriptor access
abort-on-property-read/write Throws ReferenceError on access Only aborts — doesn't modify values
trusted-prune-inbound-object Proxy on native methods, prunes arguments Prunes function arguments, not property descriptor returns

None of these can do: "When window.__CONFIG is read, return it with trackingPreferences removed."

Real-world use case

// Site sets:
window.__INITIAL_STATE__ = {
  user: { name: "Alice", id: "123", trackingId: "abc-def", adPreferences: {...} },
  config: { theme: "dark", analyticsEnabled: true }
};

// Desired: when any script reads window.__INITIAL_STATE__,
// `trackingId` and `adPreferences` are pruned, `analyticsEnabled` set to false

// example.com##+js(edit-object-on-getter, window.__INITIAL_STATE__, $.user.trackingId $.user.adPreferences $.config.analyticsEnabled)
// Not possible with any existing AdGuard scriptlet

Proposed implementation

1. New helper

A shared helper that defines getter/setter traps on a property chain and applies a configurable edit callback. Can reuse:

  • getPropertyInChain() — chain walking (get-property-in-chain.ts)
  • setPropertyAccess() — safe Object.defineProperty wrapper (object-utils.ts)
  • interceptChainProp() — intermediate chain trap (chain-prop-utils.ts)
  • getDescriptorAddon() — infinite-loop prevention (get-descriptor-addon.ts)
  • jsonPruner() / jsonPath() — JSON mutation (prune-utils.ts, json-path-utils.ts)

2. Four new scriptlet files

File Uses
src/scriptlets/edit-object-on-getter.js jsonPruner for prune-only
src/scriptlets/edit-object-on-setter.js jsonPruner for prune-only
src/scriptlets/trusted-edit-object-on-getter.js jsonPath for full edit
src/scriptlets/trusted-edit-object-on-setter.js jsonPath for full edit

Each accepts: propChain (dot-separated path) + variadic JSONPath expressions.

3. Test files (4), compatibility table updates, auto-generated wiki docs

Design decisions

  1. JSONPath syntax: Use AdGuard's existing JSONPath syntax from json-path-utils.ts. uBO compatibility aliases may be needed for cross-blocker filter lists.
  2. Non-trusted scope: Follow uBO's security boundary — only pruning, no value injection.
  3. Chain trapping: Follow set-constant pattern — trap intermediate null/undefined props so the trap activates when the chain materializes.
  4. Stack matching: Consider adding optional stack parameter for targeting specific call sites (uBO doesn't include it, but AdGuard's set-constant does).

Related

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions