antvis / antvis/util

Unsanitized Callback Input Leads to Code Execution in @antv/dom-util v2.0.4

Open
#133 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
72
Forks
28
PR merge metrics
No merged PRs in 30d

Description

# Vulnerability Report: @antv/dom-util

hi, we are a security team. We found a vulnerability in your project.
---

## Vulnerability Details

### EX0003: Code Execution

**Verified Output:** `[CASE_ID=EX0003] [VULN_CODE] Function constructor callback executed`

**Taint Analysis:**

> Attacker creates a callback using Function constructor (code execution) and passes it to addEventListener. When the malicious element's addEventListener is called, it immediately invokes the attacker-controlled callback, executing the dynamically constructed code.

**Sink Location:** `package/package/esm/add-event-listener.js` line 6

**PoC Code:**

```javascript
const _pkg = await import(require.resolve('./package/package'));
const lib = _pkg.default;
const attackerCallback = new Function('console.log("TAINT_MARKER_FUNCTION_CONSTRUCTOR")');
const mockElement = { addEventListener: function(e, cb) { cb(); } };
lib.addEventListener(mockElement, 'click', attackerCallback);
```

**PoC File:** [poc_EX0003.js](./poc_EX0003.js)

---

### EX0005: Code Execution

**Verified Output:** `[CASE_ID=EX0005] [VULN_CODE] Code execution via eval-like addEventListener`

**Taint Analysis:**

> Attacker provides an element where addEventListener is replaced with eval. When the library calls target.addEventListener(eventType, callback, false), it becomes eval('console.log("TAINT_MARKER_EVAL")', callback, false), executing the eventType string as code.

**Sink Location:** `package/package/esm/add-event-listener.js` line 4

**PoC Code:**

```javascript
const _pkg = await import(require.resolve('./package/package'));
const lib = _pkg.default;
const attackerElement = { addEventListener: eval };
lib.addEventListener(attackerElement, 'console.log("TAINT_MARKER_EVAL")', function() {});
```

**PoC File:** [poc_EX0005.js](./poc_EX0005.js)

---

### EX0006: Code Execution

**Verified Output:** `[CASE_ID=EX0006] [VULN_CODE] Callback executed via Proxy interception`

**Taint Analysis:**

> Attacker uses a Proxy object to intercept property access. When addEventListener checks for target.addEventListener, the Proxy's get trap returns a malicious function that immediately executes the callback, allowing attacker-controlled code execution through the callback parameter.

**Sink Location:** `package/package/esm/add-event-listener.js` line 6

**PoC Code:**

```javascript
const _pkg = await import(require.resolve('./package/package'));
const lib = _pkg.default;
const proxy = new Proxy({}, { get: function(target, prop) { if(prop === 'addEventListener') return function(e, cb) { cb(); }; } });
lib.addEventListener(proxy, 'click', function() { console.log('TAINT_MARKER_PROXY_CALLBACK'); });
```

**PoC File:** [poc_EX0006.js](./poc_EX0006.js)

---

## Affected Sinks

| File | Line | Type | Code Snippet |
|------|------|------|-------------|
| `package/package/esm/modify-css.js` | 5 | DYNAMIC_PROP_WRITE | `dom.style[key] = css[key];` |
| `package/package/lib/modify-css.js` | 7 | DYNAMIC_PROP_WRITE | `dom.style[key] = css[key];` |
| `package/package/esm/add-event-listener.js` | 6 | CODE_EXEC_SINK | `remove: function () {` |
| `package/package/esm/add-event-listener.js` | 16 | CODE_EXEC_SINK | `remove: function () {` |
| `package/package/lib/index.js` | 6 | CODE_EXEC_SINK | `Object.defineProperty(exports, "addEventListener", { enumerable: true, get: function () { return add_event_listener_1.default; } });` |
| `package/package/lib/index.js` | 8 | CODE_EXEC_SINK | `Object.defineProperty(exports, "createDom", { enumerable: true, get: function () { return create_dom_1.default; } });` |
| `package/package/lib/index.js` | 10 | CODE_EXEC_SINK | `Object.defineProperty(exports, "getHeight", { enumerable: true, get: function () { return get_height_1.default; } });` |
| `package/package/lib/index.js` | 12 | CODE_EXEC_SINK | `Object.defineProperty(exports, "getOuterHeight", { enumerable: true, get: function () { return get_outer_height_1.default; } });` |
| `package/package/lib/index.js` | 14 | CODE_EXEC_SINK | `Object.defineProperty(exports, "getOuterWidth", { enumerable: true, get: function () { return get_outer_width_1.default; } });` |
| `package/package/lib/index.js` | 16 | CODE_EXEC_SINK | `Object.defineProperty(exports, "getRatio", { enumerable: true, get: function () { return get_ratio_1.default; } });` |
| `package/package/lib/index.js` | 18 | CODE_EXEC_SINK | `Object.defineProperty(exports, "getStyle", { enumerable: true, get: function () { return get_style_1.default; } });` |
| `package/package/lib/index.js` | 20 | CODE_EXEC_SINK | `Object.defineProperty(exports, "getWidth", { enumerable: true, get: function () { return get_width_1.default; } });` |
| `package/package/lib/index.js` | 22 | CODE_EXEC_SINK | `Object.defineProperty(exports, "modifyCSS", { enumerable: true, get: function () { return modify_css_1.default; } });` |
| `package/package/lib/add-event-listener.js` | 8 | CODE_EXEC_SINK | `remove: function () {` |
| `package/package/lib/add-event-listener.js` | 18 | CODE_EXEC_SINK | `remove: function () {` |

## Remediation

- **Validate** all configuration options and callbacks before use
- **Avoid** passing untrusted functions or objects as configuration
- **Use** TypeScript strict typing to prevent unexpected function injection
- **Review** all entry points that accept user-controlled configuration objects

---

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with package/package/esm/add-event-listener.js and package/package/lib/add-event-listener.js at the reported sink locations, then review the linked PoC cases EX0003, EX0005, and EX0006. Compare the reported behavior with the package entry points and affected files, and define done as a reproducible vulnerability assessment with a specific validated remediation and regression coverage.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
20/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.