a2ui-project / a2ui-project/a2ui
[BUG]: Prototype Pollution in DataModel path resolution
- Vorherrschende Sprache
- TypeScript
- Sterne
- 16.4k
- Forks
- 1.3k
- Ø Merge
- 2 T. 13 Std.
- Gemergte PRs (30 T.)
- 134
Beschreibung
# 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.
Beitragsleitfaden
Bewertung
Dieses Issue wurde noch nicht bewertet.