a2ui-project / a2ui-project/a2ui
Component schema validation silently ignores primitive type mismatches
- Langage dominant
- TypeScript
- Étoiles
- 16.4k
- Forks
- 1.3k
- Merge moyen
- 2 j 13 h
- PR mergées (30 j)
- 134
Description
_↴ Ported from [flutter/genui#954](https://github.com/flutter/genui/issues/954) — originally opened by [andrewkolos](https://github.com/andrewkolos) on 2026-06-03._
_Original labels: front-line-handled_
_Original assignees: [andrewkolos](https://github.com/andrewkolos)_
---
Currently, GenUI uses a bespoke JSON Schema walker ([`_validateInstance`](https://github.com/flutter/genui/blob/c79062b7094eacc6e849ed97b8b507aa50237f02/packages/genui/lib/src/model/ui_models.dart#L235-L300) inside [`SurfaceDefinition.validate`](https://github.com/flutter/genui/blob/c79062b7094eacc6e849ed97b8b507aa50237f02/packages/genui/lib/src/model/ui_models.dart#L155)) to validate incoming components.
While this custom validator correctly enforces structural keywords like `const`, `enum`, `required`, and `properties`, it doesn't consider `type`.Because `type` is not validated, per-property primitive-type mismatches silently pass the validation layer.
Example: suppose a `MyButton` component expects a `label` of type `string` and a `width` of type `integer`, the following malformed component will successfully pass validation:
```json
{"id": "btn", "component": "Button", "label": 42, "width": "two"}
```
This bad data lands in [`ComponentModel.properties`](https://github.com/flutter/genui/blob/c79062b7094eacc6e849ed97b8b507aa50237f02/packages/a2ui_core/lib/src/core/component_model.dart#L22) with the wrong types, silently corrupting the data model and eventually causing a crash at render time when the Flutter widget expects a `String` but finds an `int`.
Also, here's a failing test that captures the issue:
```dart
// in ui_models_test.dart
test('validate enforces primitive types (this will fail due to the bug)', () {
final component = const Component(
id: 'test',
type: 'Text',
// oopsies, text is a number
properties: {'text': 42},
);
final surfaceDefinition = SurfaceDefinition(
surfaceId: 's1',
components: {'test': component},
);
final schema = S.object(
properties: {
'components': S.list(
items: S.object(
properties: {
'component': S.string(constValue: 'Text'),
'text': S.string(), // Validator should enforce this.
},
),
),
},
);
expect(
() => surfaceDefinition.validate(schema),
throwsA(isA()),
);
});
});
```
Initially, I was confused as to why we aren't using the `json_schema_builder` package to validate these components. However, I there are a few reasons:
1. `Schema.validate()` is async. [`SurfaceController.handleMessage`](https://github.com/flutter/genui/blob/c79062b7094eacc6e849ed97b8b507aa50237f02/packages/genui/lib/src/engine/surface_controller.dart#L101) is a synchronous `void` function responding to `Stream.listen(...)`. Going async would ripple through callers and could involve a good amount of refactoring.
2. The global catalog schema defines many things (`{components, styles, functions}`). If we feed a component payload directly to the package, it will fail because `styles` and `functions` are missing. The hand-rolled implementation in `SurfaceDefinition.validate` drills down into the `oneOf` branch and validates the component against *only* its specific sub-schema.
3. The current implementation throws an [`A2uiValidationException`](https://github.com/flutter/genui/blob/c79062b7094eacc6e849ed97b8b507aa50237f02/packages/genui/lib/src/model/ui_models.dart#L366) containing a formatted `path` on the first failure, which is optimized for relaying over our protocol back to the server.
## Fixes
Band-aid fix: extend the `_validateInstance` method to explicitly check the `type` keyword (and ideally `additionalProperties`).
Proper fix (maybe): Delegate entirely to `json_schema_builder` to close all spec gaps. This will require:
- Make `SurfaceController.handleMessage` async.
- Writing an adapter to translate `ValidationError` lists into `A2uiValidationException`s.
Guide de contribution
Ouvrir le guide de contribution
Évaluation
Cette issue n'a pas encore été évaluée.