AdguardTeam / AdguardTeam/Scriptlets

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

Open
#568 1 comment 0 reactions 1 assignee Claimed by @maximtop View on GitHub
enhancement Feature request Priority: P4 Version: Scriptlets v2.5
Dominant language
JavaScript
Stars
195
Forks
33
PR merge metrics
No merged PRs in 30d

Description

uBlock Origin recently added four new scriptlets that AdGuard has no equivalent for — ([commit 99e6228](https://github.com/gorhill/uBlock/commit/99e622831b5fdd746996334d2108a45f65aeb1e6)):

- **`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

```javascript
// 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

- uBO commit: https://github.com/gorhill/uBlock/commit/99e622831b5fdd746996334d2108a45f65aeb1e6
- uBO issue: https://github.com/uBlockOrigin/uBlock-issues/issues/4019

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.