-
Notifications
You must be signed in to change notification settings - Fork 33
Add new scriptlets — 'edit-object-on-getter' and 'edit-object-on-setter' #568
Open
Description
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 accesstrusted-edit-object-on-getter.js— edit (set new values) properties of an object on read accessedit-object-on-setter.js— prune properties from an object on write accesstrusted-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()— safeObject.definePropertywrapper (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
- JSONPath syntax: Use AdGuard's existing JSONPath syntax from
json-path-utils.ts. uBO compatibility aliases may be needed for cross-blocker filter lists. - Non-trusted scope: Follow uBO's security boundary — only pruning, no value injection.
- Chain trapping: Follow
set-constantpattern — trap intermediate null/undefined props so the trap activates when the chain materializes. - Stack matching: Consider adding optional
stackparameter for targeting specific call sites (uBO doesn't include it, but AdGuard'sset-constantdoes).