a2ui-project / a2ui-project/a2ui

[BUG]: Prototype Pollution in DataModel path resolution

未关闭
#2,580 1 条评论 0 个 reaction 已指派 1 人 已被 @Varun-S10 认领 在 GitHub 查看
P2 status: first-line-handled status: waiting-for-author-response type: bug
主要语言
TypeScript
星标
16.4k
派生
1.3k
平均合并
2 天 13 小时
30 天内合并 PR
134

描述

# Location
renderers/lit_internal/src/v0_8/data/model-processor.ts:251

# Description
The DataModel.set method in renderers/web_core/src/v0_8/data/model-processor.ts processes arbitrary user-supplied path strings (from an A2UI message payload) to recursively traverse or create properties on nested objects/maps. Specifically, const segments = this.normalizePath(path).split('/').filter(s => s); extracts the keys, and in the loop, if the target key is missing, it dynamically populates the object. Crucially, there is no validation step to block keys like __proto__, constructor, or prototype before they are assigned. Given that current can eventually resolve to a native Object (via this.objCtor or isObject(value) branches or when custom map structures fall back to objects depending on user inputs or framework quirks), this lack of filtering makes it possible to write to __proto__ and therefore overwrite properties on Object.prototype.

# Impact
Prototype Pollution allowing global state manipulation in the host web application, which can often be escalated to XSS (Cross-Site Scripting) or other critical client-side vulnerabilities.

# Mitigation
Implement a check during path segment traversal to block or ignore keys known to cause prototype pollution, specifically __proto__, constructor, and prototype. For example, throw an error or skip the segment if ['__proto__', 'constructor', 'prototype'].includes(segment).

# Reproduction Steps
Construct an A2UI message of type dataModelUpdate targeting an existing surface.
Set the path to /__proto__/polluted. Alternatively, provide a contents array with a key like __proto__ and a nested valueMap that sets polluted.
Send the message to the renderer.
Observe if Object.prototype.polluted is now set in the browser console.

# Evidence
```
let current: DataMap | DataArray = root;
for (let i = 0; i < segments.length - 1; i++) {
const segment = segments[i];
let target: DataValue | undefined;

if (current instanceof this.mapCtor) {
target = current.get(segment);
} else if (Array.isArray(current) && /^\\d+$/.test(segment)) {
target = current[parseInt(segment, 10)];
}

if (
target === undefined ||
typeof target !== \"object\" ||
target === null
) {
target = new this.mapCtor();
if (current instanceof this.mapCtor) {
current.set(segment, target);
} else if (Array.isArray(current)) {
current[parseInt(segment, 10)] = target;
}
}
current = target as DataMap | DataArray;
}
```

# Reasoning
The setDataByPath method in renderers/lit_internal/src/v0_8/data/model-processor.ts and renderers/web_core/src/v0_8/data/model-processor.ts processes arbitrary user-supplied path strings (from an A2UI message payload) to recursively traverse or create properties on nested objects/maps. Specifically, const segments = this.normalizePath(path).split('/').filter(s => s); extracts the keys, and in the loop, if the target key is missing, it dynamically populates the object. Crucially, there is no validation step to block keys like __proto__, constructor, or prototype before they are assigned. Given that current can eventually resolve to a native Object (via this.objCtor or isObject(value) branches or when custom map structures fall back to objects depending on user inputs or framework quirks), this lack of filtering makes it possible to write to __proto__ and therefore overwrite properties on Object.prototype.

贡献指南

打开贡献指南

评估

这个 Issue 还没有评估数据。

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。