a2ui-project / a2ui-project/a2ui
[BUG]: Unhandled TypeError in Icon Component due to Missing Type Verification
- 主要言語
- TypeScript
- スター
- 16.4k
- フォーク
- 1.3k
- 平均マージ
- 2日 13時間
- マージ済み PR(30日)
- 134
説明
# Location
renderers/angular/src/v0_8/components/icon.ts:35
# Description
The resolvedName computed signal in the Angular Icon component resolves its name property dynamically from the agent-controlled DataModel. It fails to verify if the resolved rawName is a string before passing it to toSnakeCase(). If a malicious or compromised agent provides a data model path that resolves to a non-string value such as an object, array, or number, the replace() method called inside toSnakeCase() will throw a TypeError. This unhandled exception during Angular's change detection cycle aborts rendering and causes a Denial of Service (crash) for the chat UI.
# Impact
Denial of Service (UI crash / Component rendering failure)
# Mitigation
Verify the type of rawName before processing. Modify the resolvedName computed signal to check if typeof rawName === 'string', or cast it using String(rawName) before passing it to toSnakeCase().
# Reproduction Steps
Send an A2UI message that defines an icon component.
Set the name property to resolve from a path in the DataModel that contains an object or an array (e.g., {"path": "/my_array"}).
The component attempts to render, and rawName.replace throws a TypeError, breaking the Angular change detection loop for the view.
# Evidence
```
protected readonly resolvedName = computed(() => {
const rawName = this.resolvePrimitive(this.name());
if (!rawName) return '';
return this.toSnakeCase(rawName); // rawName can be an object or number if resolved from DataModel
});
private toSnakeCase(str: string): string {
return str
.replace(/^[A-Z]/, letter => letter.toLowerCase()) // Throws TypeError if str is not a string
.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`);
}
```
# Reasoning
The finding correctly identifies that the A2UI v0.8 Angular Icon component dynamically resolves its name property from the DataModel via resolvePrimitive(this.name()). Since the Agent is untrusted (Trust Boundary 1) and can control the DataModel contents via updateDataModel messages, it can supply a path that resolves to a non-string type, such as an array ([]), object ({}), or number (123).
When rawName is an array or object, it passes the truthiness check (if (!rawName) return '';) but causes this.toSnakeCase(rawName) to throw a TypeError because non-string primitives do not have a .replace() method.
In Angular, an unhandled exception thrown synchronously inside a computed() signal's calculation aborts the change detection cycle, leading to a rendering crash for the component and a potential freeze of the host product's UI. This strictly aligns with the A2UI Threat Model for Malicious Agents (A1), whose stated goal includes a 'main-thread freeze of embedding product', and the fact that framework binders are considered CRITICAL for availability. The issue was mitigated in the v0.9 IconComponent (typeof name !== 'string'), but due to A2UI's multi-version co-existence, the v0.8 implementation remains vulnerable.
# Verification Hints
Construct an Agent response with an icon component where name points to a data model path: {\"type\": \"icon\", \"id\": \"test-icon\", \"name\": {\"path\": \"/bad_data\"}}.
Supply a DataModel update setting /bad_data to an array, e.g., [1, 2, 3].
Render the v0_8 Angular Icon component and trigger Angular change detection (e.g., fixture.detectChanges()).
Observe that a TypeError: rawName.replace is not a function is thrown and the component fails to render, confirming the DoS vector.
コントリビューションガイド
評価
この issue はまだ評価されていません。