| --- |
| license: apache-2.0 |
| tags: |
| - security |
| - huntr |
| - model-file-vulnerability |
| - circle |
| --- |
| |
| # PoC for Huntr Report — Missing Validation for Optional FlatBuffers Fields (`subgraphs`) |
|
|
| **Target:** `model-explorer-circle` 0.1.4 (Circle adapter for [Google Model Explorer](https://github.com/google-ai-edge/model-explorer)) |
|
|
| **Issue:** Missing validation for optional FlatBuffers fields. `CircleAdapter.build_graph()` unconditionally accesses `self.model.subgraphs[0]` without checking whether the `subgraphs` vector is present. The Circle schema allows this field to be absent, in which case Python's flatbuffers bindings expose it as `None`, and the subscript raises an unhandled `TypeError` inside the adapter. |
|
|
| **File:** `poc_empty_subgraphs.circle` — 24 bytes. A minimal, schema-valid Circle FlatBuffer (`CIR0` identifier) with only `version` set; `subgraphs` is left unset (`None`). |
|
|
| ## Environment |
|
|
| ``` |
| model-explorer-circle==0.1.4 |
| ai-edge-model-explorer==0.1.32 |
| flatbuffers==25.12.19 |
| Python 3.13.3 |
| Windows 11 Home 10.0.26200 (Build 26200) |
| ``` |
|
|
| ## Usage |
|
|
| ```bash |
| pip install ai-edge-model-explorer==0.1.32 model-explorer-circle==0.1.4 |
| |
| model-explorer --extensions model_explorer_circle --port 8091 --no_open_in_browser |
| ``` |
|
|
| Then, in the web UI, upload `poc_empty_subgraphs.circle` via the Circle adapter — or reproduce directly against the API the UI itself calls: |
|
|
| ```bash |
| curl -X POST http://127.0.0.1:8091/apipost/v1/send_command \ |
| -H "Content-Type: application/json" \ |
| -d '{"extensionId":"circle-adapter","cmdId":"convert","modelPath":"<path to poc_empty_subgraphs.circle>","deleteAfterConversion":false,"settings":{}}' |
| ``` |
|
|
| ## Expected result |
|
|
| The adapter raises an unhandled exception, which Model Explorer's server catches and returns as a JSON error: |
|
|
| ```json |
| {"error": "TypeError: 'NoneType' object is not subscriptable"} |
| ``` |
|
|
| Full server-side traceback: |
|
|
| ``` |
| Traceback (most recent call last): |
| File "site-packages/model_explorer/server.py", line 348, in send_command_post |
| resp = extension_manager.run_cmd(request.json) |
| File "site-packages/model_explorer/extension_manager.py", line 96, in run_cmd |
| resp = self.adapter_runner.run_adapter(extension=extension, cmd=cmd) |
| File "site-packages/model_explorer/adapter_runner.py", line 41, in run_adapter |
| return fn(modelPath, cmd['settings']) |
| File "site-packages/model_explorer_circle/main.py", line 277, in convert |
| self.build_graph(self.graph) |
| File "site-packages/model_explorer_circle/main.py", line 200, in build_graph |
| sub_graph = self.model.subgraphs[0] |
| TypeError: 'NoneType' object is not subscriptable |
| ``` |
|
|
| ## Note on impact |
|
|
| The exception is caught by Model Explorer's top-level Flask handler (`server.py`, `send_command`/`send_command_post` routes) and returned as a clean JSON error. The server process does not crash and continues serving subsequent requests normally — verified by successfully loading a valid `.circle` model in the request immediately following this one. The concrete impact is that a malformed `.circle` file reliably fails to load with an internal Python exception surfaced to the user, rather than a clear "invalid model" validation message. |
|
|
| ## Suggested fix |
|
|
| The adapter assumes several optional FlatBuffers vectors are always present, while the schema permits them to be absent. Defensive validation before dereferencing these fields would allow malformed models to be rejected gracefully with a user-facing validation error rather than raising internal exceptions. |
|
|