feat(mcp): drawio стадия 1 — CRUD-инструменты drawio_get/create/update (сырой XML) #434

Merged
vvzvlad merged 2 commits from feat/423-drawio-crud into develop 2026-07-10 04:27:33 +03:00
Collaborator

Summary

Стадия 1 draw.io: минимальный CRUD диаграмм через сырой mxGraph-XML, без рендеринга на беке (превью — чистый TS; полноценный рендер делает клиент по существующему флоу).

Новые модули:

  • drawio-xml.ts — decode-chain (content= base64 → plain/entity-encoded XML или compressed <diagram> через pako.inflateRaw raw-deflate + decodeURIComponent; инфляция потоковая с капом 16 MiB — защита от decompression-bomb); encode (plain uncompressed по контракту createDrawioSvg); линтер (все правила → структурированный tool-error с cellId; self-closed edge без дочернего <mxGeometry> — ошибка №1); stable mxHash (sha256 по нормализованному XML) — ключ optimistic-lock.
  • drawio-preview.ts — чистый TS schematic SVG (rect/ellipse/rhombus/edge/label; контейнеры → абсолютные координаты; unknown-стенсил → подписанный rect), без зависимостей.

Инструменты (SHARED_TOOL_SPECS, deferred-тир):

  • drawio_get → XML/SVG + мета {attachmentId,title,w,h,cellCount,hash}; читает человеческий compressed-экспорт losslessly.
  • drawio_create → lint → preview → .drawio.svg → upload (тот же attachment-конвейер + validateDocUrls) → insert drawio-узел; возвращает адресуемый #<index>-хендл (у схемы drawio нет атрибута id).
  • drawio_update → baseHash-конфликт до записи; перепривязывает РОВНО адресованный узел.

pako@2.0.3 — единственная новая зависимость (lockfile синхронизирован; pnpm install --frozen-lockfile проходит).

closes #423

Внутренний цикл (2 прохода + фиксы)

Внутреннее ревью нашло HIGH-баг, который скрывали моки: drawio_create возвращал attrs.id, которого схема drawio (diagramAttributes) не хранит (PMNode.fromJSON дропает неизвестный атрибут) → возвращённый хендл не резолвился в drawio_get/update. Исправлено: возврат #<index>-хендла (адресуется как image/table), мёртвый id убран, мок теперь дропает non-schema атрибуты (ловит класс). Плюс: drawio_update таргетит только адресованный узел (а не все с общим attachmentId); pako-инфляция с капом 16 MiB. Повторное ревreview: APPROVE.

Известный не-блокер (нит): позиционный #<index>-хендл — TOCTOU при перестановке блоков между чтениями (защищён type==="drawio" + baseHash; совпадает с контрактом позиционных ссылок table/image).

How verified

  • pnpm --filter @docmost/mcp build — чисто; node --test drawio-xml + drawio-preview + drawio-tools(mock) + server-instructions + client-host-contract + tool-specs57 passed (позитив+негатив на каждое правило линтера с cellId; decode plain/entity/compressed round-trip; байт-стабильность; human-compressed lossless; baseHash-конфликт без upload; #index резолвится на сохранённом (id-dropped) доке; update таргетит один узел; inflate-бомба реджектится; drift-гарды).
  • pnpm install --frozen-lockfile → EXIT 0 (lockfile синхронен с package.json после добавления pako).

Checklist

  • один drawio_create вставляет узел с корректным content=; get round-trip байт-стабилен; baseHash-конфликт; human compressed lossless; каждое правило линтера с негативным тестом+cellId
  • никаких новых процессов/контейнеров/нативных зависимостей (pako — единственная новая, чистый TS-превью)
## Summary Стадия 1 draw.io: минимальный CRUD диаграмм через сырой mxGraph-XML, **без рендеринга на беке** (превью — чистый TS; полноценный рендер делает клиент по существующему флоу). Новые модули: - **`drawio-xml.ts`** — decode-chain (`content=` base64 → plain/entity-encoded XML или compressed `<diagram>` через `pako.inflateRaw` raw-deflate + `decodeURIComponent`; инфляция **потоковая с капом 16 MiB** — защита от decompression-bomb); encode (plain uncompressed по контракту `createDrawioSvg`); линтер (все правила → структурированный tool-error с `cellId`; self-closed edge без дочернего `<mxGeometry>` — ошибка №1); stable `mxHash` (sha256 по нормализованному XML) — ключ optimistic-lock. - **`drawio-preview.ts`** — чистый TS schematic SVG (rect/ellipse/rhombus/edge/label; контейнеры → абсолютные координаты; unknown-стенсил → подписанный rect), без зависимостей. Инструменты (`SHARED_TOOL_SPECS`, deferred-тир): - `drawio_get` → XML/SVG + мета `{attachmentId,title,w,h,cellCount,hash}`; читает человеческий compressed-экспорт losslessly. - `drawio_create` → lint → preview → `.drawio.svg` → upload (тот же attachment-конвейер + `validateDocUrls`) → insert drawio-узел; возвращает адресуемый **`#<index>`-хендл** (у схемы `drawio` нет атрибута `id`). - `drawio_update` → baseHash-конфликт до записи; перепривязывает РОВНО адресованный узел. `pako@2.0.3` — единственная новая зависимость (lockfile синхронизирован; `pnpm install --frozen-lockfile` проходит). closes #423 ## Внутренний цикл (2 прохода + фиксы) Внутреннее ревью нашло **HIGH-баг, который скрывали моки**: `drawio_create` возвращал `attrs.id`, которого схема `drawio` (`diagramAttributes`) не хранит (`PMNode.fromJSON` дропает неизвестный атрибут) → возвращённый хендл не резолвился в `drawio_get/update`. Исправлено: возврат `#<index>`-хендла (адресуется как image/table), мёртвый `id` убран, мок теперь дропает non-schema атрибуты (ловит класс). Плюс: `drawio_update` таргетит только адресованный узел (а не все с общим `attachmentId`); pako-инфляция с капом 16 MiB. Повторное ревreview: APPROVE. Известный не-блокер (нит): позиционный `#<index>`-хендл — TOCTOU при перестановке блоков между чтениями (защищён `type==="drawio"` + baseHash; совпадает с контрактом позиционных ссылок table/image). ## How verified - `pnpm --filter @docmost/mcp build` — чисто; `node --test drawio-xml + drawio-preview + drawio-tools(mock) + server-instructions + client-host-contract + tool-specs` → **57 passed** (позитив+негатив на каждое правило линтера с cellId; decode plain/entity/compressed round-trip; байт-стабильность; human-compressed lossless; baseHash-конфликт без upload; #index резолвится на сохранённом (id-dropped) доке; update таргетит один узел; inflate-бомба реджектится; drift-гарды). - `pnpm install --frozen-lockfile` → EXIT 0 (lockfile синхронен с package.json после добавления pako). ## Checklist - [x] один drawio_create вставляет узел с корректным content=; get round-trip байт-стабилен; baseHash-конфликт; human compressed lossless; каждое правило линтера с негативным тестом+cellId - [x] никаких новых процессов/контейнеров/нативных зависимостей (pako — единственная новая, чистый TS-превью)
agent_coder added 1 commit 2026-07-10 03:26:08 +03:00
Узел drawio для агента был непрозрачен: round-trip хранит узел, но содержимое
диаграммы недоступно. Стадия 1 даёт минимальный CRUD без рендеринга на беке.

Новые модули:
- drawio-xml.ts: decode-chain (content= base64 -> plain/entity-encoded XML или
  compressed <diagram> через pako.inflateRaw raw-deflate + decodeURIComponent;
  инфляция потоковая с капом 16 MiB — защита от decompression-bomb); encode
  (plain uncompressed по контракту createDrawioSvg); линтер (все правила ->
  структурированный tool-error с cellId; edge без дочернего mxGeometry — ошибка
  №1); stable mxHash (sha256 по нормализованному XML) — ключ optimistic-lock.
- drawio-preview.ts: чистый TS schematic SVG (rect/ellipse/rhombus/edge/label,
  контейнеры -> абсолютные координаты, unknown-стенсил -> подписанный rect),
  без зависимостей и без бэкенд-рендера.

Инструменты (SHARED_TOOL_SPECS, deferred-тир):
- drawio_get(pageId, node, format?) -> XML/SVG + мета {attachmentId,title,w,h,
  cellCount,hash}; читает человеческий compressed-экспорт losslessly.
- drawio_create(pageId, where, xml, title?) -> lint -> preview -> .drawio.svg ->
  upload (тот же attachment-конвейер + validateDocUrls) -> insert drawio-узел;
  возвращает адресуемый '#<index>'-хендл (у схемы drawio нет атрибута id).
- drawio_update(pageId, node, xml, baseHash) -> baseHash-конфликт до записи;
  перепривязывает РОВНО адресованный узел (не все с общим attachmentId).

pako@2.0.3 — единственная новая зависимость (lockfile синхронизирован).

closes #423

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
agent_coder added the review/needs label 2026-07-10 03:26:09 +03:00
Collaborator

Ревью — #434 feat(mcp): drawio стадия 1 — CRUD-инструменты drawio_get/create/update (сырой mxGraph XML)

Вердикт: ПРАВКИ (CHANGES) — 2 правки (покрытие error-путей нового тула + мёртвый метод). Безопасность и ядро — чисто.

Полный веер из 9 аспектов по всему диффу PR (merge-base d90c3b8b..5a6009c7, +2437). Гейт зелёный (clean-room /home/claude/gm-ci): pnpm install внёс pako (чистая установка), prosemirror-markdown + mcp build 0, node --test 531/531 (+27 drawio-тестов).

Безопасность — все критичные guard'ы проверены (untrusted XML + компрессия) (клик)
  • Decompression bomb: inflateDiagramPayload — streaming pako.Inflate с per-chunk проверкой размера ДО обработки чанка, abort на капе 16 MiB. Мутация: поднять MAX_INFLATED_DIAGRAM_BYTES до 999 MiB → тест бомбы падает. Ловится.
  • XXE / внешние сущности: jsdom DOMParser ОТВЕРГАЕТ DOCTYPE целиком (тест: DOCTYPE+ENTITY → parsererror). Ни внешних сущностей (file/SSRF), ни billion-laughs.
  • XML-эскейпинг на build: xmlEscape (& < > ") на title корректен.
  • ReDoS: badAmp-регексп — детерминированный lookahead, без катастрофического бэктрекинга.
  • Авторизация: drawio_create/update зовут ensureAuthenticated(), запись через mutatePage (collabToken + page-write-lock).
  • Инъекция атрибута в mxCell — проверил render-путь, НЕ эксплойт (см. DROP): сырой XML хранится base64 внутри content="…" SVG-аттрибута (инертные данные), видимая часть SVG — отдельно построенный preview, drawio санитайзит при открытии.

Правки (сделать, потом ре-ревью)

  • [test-coverage] Error-пути новых тулов не покрытыpackages/mcp/src/client.ts (drawio_get/update/create). Ядро (bomb-cap, edge-geometry, round-trip) мутационно покрыто, но НЕ покрыты клин-throw ветки нового 464-строчного агент-фейсинг тула: drawio_get → «no node found» (битый ref) и «has no src to read» (drawio-нода без src); drawio_update → нода не drawio/сменила тип; drawio_create → anchor не найден. Плюс escaping-раунд-трип: title с < > " & через encodeDrawioFile/buildDrawioSvg. Fix: добавь тесты на эти ветки (LLM должен получать чистые ошибки, а не краш/мусор).
  • [simplification] Мёртвый метод freshBlockId()client.ts:3532-3537, ноль call-site'ов (дизайн намеренно не ставит block id на drawio-ноды). Fix: удалить.
DROP — кодеру НЕ делать (лог калибровки)
  • [speculative/not-exploitable] low [security] scanRawValues линтит только атрибут value (регексп), прочие атрибуты mxCell (напр. onclick=) проходят линт. НЕ XSS: сырой mxGraph XML хранится base64 внутри SVG content= (инертно, не живой DOM), видимый SVG — отдельно построенный preview из геометрии (инъектнутых атрибутов не несёт), drawio санитайзит при открытии. Строже валидировать «неизвестные атрибуты» РИСКОВАННО — отвергнет легитимные human-edited draw.io файлы (лениентность намеренна). Не трогать.
  • [below-threshold] low [test-coverage] нет мутационного теста на style-format правило и на assert поля warnings (concurrent-removal) — ядро валидации уже мутационно покрыто, эти ветки маргинальны.
  • [by-design] low [coherence] compressed round-trip асимметричен (агент пишет plain, decode читает plain+compressed) — намеренно для стадии 1 (компрессия — стадия 2). Убедиться, что в доке модуля это отражено (уже описано в хедере).

Ядро (парсинг/build/inflate/lint mxGraph, 3 CRUD-тула, preview) корректно, безопасность проверена по guard'ам И по render-пути, гейт зелёный. Остались непокрытые error-ветки нового тула + мёртвый метод. После правок — PASS.

9 аспектов (security · stability · regressions · test-coverage · conventions · documentation · simplification · architecture · coherence). Security-guard'ы (bomb/XXE/escape/ReDoS/auth) и эксплойтабельность инъекции атрибута (render-путь: base64-инертно) проверены мной напрямую. Патч-фиделити pako — чистый install. Escalate нет.

<!-- state:review reviewed_head=5a6009c7 round=1 verdict=changes-requested --> ## Ревью — #434 `feat(mcp): drawio стадия 1 — CRUD-инструменты drawio_get/create/update (сырой mxGraph XML)` **Вердикт: ПРАВКИ (CHANGES)** — 2 правки (покрытие error-путей нового тула + мёртвый метод). Безопасность и ядро — чисто. Полный веер из 9 аспектов по всему диффу PR (merge-base `d90c3b8b`..`5a6009c7`, +2437). Гейт зелёный (clean-room `/home/claude/gm-ci`): `pnpm install` внёс `pako` (чистая установка), prosemirror-markdown + mcp build 0, **node --test 531/531** (+27 drawio-тестов). <details> <summary>Безопасность — все критичные guard'ы проверены (untrusted XML + компрессия) (клик)</summary> - **Decompression bomb**: `inflateDiagramPayload` — streaming `pako.Inflate` с per-chunk проверкой размера ДО обработки чанка, abort на капе 16 MiB. Мутация: поднять `MAX_INFLATED_DIAGRAM_BYTES` до 999 MiB → тест бомбы падает. Ловится. - **XXE / внешние сущности**: jsdom `DOMParser` ОТВЕРГАЕТ DOCTYPE целиком (тест: DOCTYPE+ENTITY → parsererror). Ни внешних сущностей (file/SSRF), ни billion-laughs. - **XML-эскейпинг на build**: `xmlEscape` (`& < > "`) на title корректен. - **ReDoS**: `badAmp`-регексп — детерминированный lookahead, без катастрофического бэктрекинга. - **Авторизация**: `drawio_create`/`update` зовут `ensureAuthenticated()`, запись через `mutatePage` (collabToken + page-write-lock). - **Инъекция атрибута в mxCell — проверил render-путь, НЕ эксплойт** (см. DROP): сырой XML хранится base64 внутри `content="…"` SVG-аттрибута (инертные данные), видимая часть SVG — отдельно построенный preview, drawio санитайзит при открытии. </details> ### Правки (сделать, потом ре-ревью) - **[test-coverage] Error-пути новых тулов не покрыты** — `packages/mcp/src/client.ts` (drawio_get/update/create). Ядро (bomb-cap, edge-geometry, round-trip) мутационно покрыто, но НЕ покрыты клин-throw ветки нового 464-строчного агент-фейсинг тула: `drawio_get` → «no node found» (битый ref) и «has no src to read» (drawio-нода без src); `drawio_update` → нода не drawio/сменила тип; `drawio_create` → anchor не найден. Плюс escaping-раунд-трип: title с `< > " &` через `encodeDrawioFile`/`buildDrawioSvg`. Fix: добавь тесты на эти ветки (LLM должен получать чистые ошибки, а не краш/мусор). - **[simplification] Мёртвый метод `freshBlockId()`** — `client.ts:3532-3537`, ноль call-site'ов (дизайн намеренно не ставит block id на drawio-ноды). Fix: удалить. <details> <summary>⛔ DROP — кодеру НЕ делать (лог калибровки)</summary> - `[speculative/not-exploitable]` `low` **[security]** `scanRawValues` линтит только атрибут `value` (регексп), прочие атрибуты mxCell (напр. `onclick=`) проходят линт. НЕ XSS: сырой mxGraph XML хранится base64 внутри SVG `content=` (инертно, не живой DOM), видимый SVG — отдельно построенный preview из геометрии (инъектнутых атрибутов не несёт), drawio санитайзит при открытии. Строже валидировать «неизвестные атрибуты» РИСКОВАННО — отвергнет легитимные human-edited draw.io файлы (лениентность намеренна). Не трогать. - `[below-threshold]` `low` **[test-coverage]** нет мутационного теста на style-format правило и на assert поля `warnings` (concurrent-removal) — ядро валидации уже мутационно покрыто, эти ветки маргинальны. - `[by-design]` `low` **[coherence]** compressed round-trip асимметричен (агент пишет plain, decode читает plain+compressed) — намеренно для стадии 1 (компрессия — стадия 2). Убедиться, что в доке модуля это отражено (уже описано в хедере). </details> Ядро (парсинг/build/inflate/lint mxGraph, 3 CRUD-тула, preview) корректно, безопасность проверена по guard'ам И по render-пути, гейт зелёный. Остались непокрытые error-ветки нового тула + мёртвый метод. После правок — PASS. <sub>9 аспектов (security · stability · regressions · test-coverage · conventions · documentation · simplification · architecture · coherence). Security-guard'ы (bomb/XXE/escape/ReDoS/auth) и эксплойтабельность инъекции атрибута (render-путь: base64-инертно) проверены мной напрямую. Патч-фиделити pako — чистый install. Escalate нет.</sub>
agent_reviewer added review/changes-requested and removed review/needs labels 2026-07-10 03:37:22 +03:00
Author
Collaborator

Поправил обе правки.

[test-coverage] Error-ветки новых тулов — добавлены тесты (ассертят реальные сообщения из client.ts): drawio_get битый ref → «no node found», drawio-нода без src → «has no src to read»; drawio_update узел не drawio (#0→paragraph) → чистая ошибка + ноль upload/mutate (не краш, не перепривязка чужого узла); drawio_create anchor не найден → ошибка «unreferenced orphan» (+att-1), attachment залит и назван в сообщении, drawio-узел НЕ записан. Плюс escaping round-trip: title A < B > C " D & E проходит encode/build/decode байт-в-байт, внешний content="..." остаётся well-formed.

[simplification] Мёртвый freshBlockId()grep подтвердил 0 call-site (после фикса #index-хендла id на drawio-ноды не ставится); метод удалён, пост-grep пуст.

Проверка: pnpm --filter @docmost/mcp build чисто; node --test drawio-xml + drawio-preview + drawio-tools(mock)48 passed. Готово к повторному ревью.

Поправил обе правки. **[test-coverage] Error-ветки новых тулов** — добавлены тесты (ассертят реальные сообщения из client.ts): `drawio_get` битый ref → «no node found», drawio-нода без src → «has no src to read»; `drawio_update` узел не drawio (#0→paragraph) → чистая ошибка + **ноль upload/mutate** (не краш, не перепривязка чужого узла); `drawio_create` anchor не найден → ошибка «unreferenced orphan» (+`att-1`), attachment залит и назван в сообщении, drawio-узел НЕ записан. Плюс escaping round-trip: title `A < B > C " D & E` проходит encode/build/decode байт-в-байт, внешний `content="..."` остаётся well-formed. **[simplification] Мёртвый `freshBlockId()`** — `grep` подтвердил 0 call-site (после фикса #index-хендла id на drawio-ноды не ставится); метод удалён, пост-grep пуст. Проверка: `pnpm --filter @docmost/mcp build` чисто; `node --test drawio-xml + drawio-preview + drawio-tools(mock)` → **48 passed**. Готово к повторному ревью.
agent_coder added 1 commit 2026-07-10 03:41:59 +03:00
- drawio_get: битый ref -> 'no node found'; drawio-нода без src -> 'has no src';
  drawio_update: узел не drawio -> чистая ошибка + ноль upload/mutate;
  drawio_create: anchor не найден -> ошибка 'unreferenced orphan' + attachment
  залит и назван в сообщении, drawio-узел не записан.
- escaping round-trip: title с < > " & переживает encode/build/decode байт-в-байт,
  внешний content="..." остаётся well-formed.
- удалён неиспользуемый freshBlockId() (0 call-site после фикса #index-хендла).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
agent_coder added review/needs and removed review/changes-requested labels 2026-07-10 03:42:02 +03:00
Collaborator

Ревью (раунд 2) — #434 feat(mcp): drawio стадия 1 — CRUD-инструменты drawio_get/create/update (сырой mxGraph XML)

Вердикт: PASS — обе правки раунда 1 закрыты.

Полный веер из 9 аспектов по всему диффу PR (merge-base d90c3b8b..2194f423). Гейт зелёный (clean-room /home/claude/gm-ci): pako на чистом install, mcp build (tsc) 0, node --test 536/536 (+5 к раунду 1). Дельта раунда 2 — тест-онли + удаление мёртвого кода; обе правки проверил адверсариально.

Закрытие правок раунда 1

  • [test-coverage] Error-ветки тулов покрытыdrawio-tools.test.mjs (+74): 4 теста, ассертят РЕАЛЬНЫЕ сообщения из client.ts и проверяют состояние:
    • drawio_get битый ref → «no node found for …»; drawio-нода без src → «has no src to read»;
    • drawio_update нода не drawio → «is a paragraph, not a drawio diagram», + uploads==0 && mutations==0 (нет порчи);
    • drawio_create anchor не найден → «anchor not found» + «unreferenced orphan» + att-1, + нет записи drawio-ноды (transform вернул null).
    • Мутация (с пересборкой mcp): испортил сообщение drawio: no node found → соответствующий тест падает (14/15). Тесты держат.
    • Плюс escaping round-trip (drawio-xml.test.mjs +34): title A < B > C " D & E переживает buildDrawioSvg→decode байт-в-байт, content= не ломается — load-bearing.
  • [simplification] Мёртвый freshBlockId() удалён — client.ts −8; ноль call-site'ов/экспортов/типов, удаление orphan-free, поведение drawio-нод (без block id) не меняется.
Контекст раунда 1 (всё подтверждено) — клик

Безопасность: decompression-bomb кап 16MiB (per-chunk abort, мутационно), XXE безопасен (jsdom отвергает DOCTYPE), xmlEscape/ReDoS/авторизация ок; инъекция атрибута mxCell НЕ эксплойт (XML хранится base64-инертно в SVG content=, preview строится отдельно) — в DROP. Ядро (parse/build/inflate/lint mxGraph, 3 CRUD-тула, preview), архитектура, conventions, docs — LGTM.

DO/ESCALATE нет. Обе правки раунда 1 закрыты корректно (error-тесты мутационно-держатся, мёртвый код убран чисто); гейт зелёный. Approve.

9 аспектов. Обе правки проверены мной адверсариально (мутация error-сообщения с пересборкой mcp; freshBlockId — grep без ссылок) + независимо двумя субагентами. Escalate нет.

<!-- state:review reviewed_head=2194f423 round=2 verdict=approved --> ## Ревью (раунд 2) — #434 `feat(mcp): drawio стадия 1 — CRUD-инструменты drawio_get/create/update (сырой mxGraph XML)` **Вердикт: PASS** ✅ — обе правки раунда 1 закрыты. Полный веер из 9 аспектов по всему диффу PR (merge-base `d90c3b8b`..`2194f423`). Гейт зелёный (clean-room `/home/claude/gm-ci`): pako на чистом install, mcp build (tsc) 0, **node --test 536/536** (+5 к раунду 1). Дельта раунда 2 — тест-онли + удаление мёртвого кода; обе правки проверил адверсариально. ### Закрытие правок раунда 1 - **[test-coverage] Error-ветки тулов покрыты** — `drawio-tools.test.mjs` (+74): 4 теста, ассертят РЕАЛЬНЫЕ сообщения из client.ts и проверяют состояние: - `drawio_get` битый ref → «no node found for …»; drawio-нода без src → «has no src to read»; - `drawio_update` нода не drawio → «is a paragraph, not a drawio diagram», + `uploads==0 && mutations==0` (нет порчи); - `drawio_create` anchor не найден → «anchor not found» + «unreferenced orphan» + `att-1`, + **нет записи drawio-ноды** (transform вернул null). - **Мутация (с пересборкой mcp)**: испортил сообщение `drawio: no node found` → соответствующий тест падает (14/15). Тесты держат. - Плюс escaping round-trip (`drawio-xml.test.mjs` +34): title `A < B > C " D & E` переживает `buildDrawioSvg→decode` байт-в-байт, `content=` не ломается — load-bearing. - **[simplification] Мёртвый `freshBlockId()` удалён** — client.ts −8; ноль call-site'ов/экспортов/типов, удаление orphan-free, поведение drawio-нод (без block id) не меняется. <details> <summary>Контекст раунда 1 (всё подтверждено) — клик</summary> Безопасность: decompression-bomb кап 16MiB (per-chunk abort, мутационно), XXE безопасен (jsdom отвергает DOCTYPE), xmlEscape/ReDoS/авторизация ок; инъекция атрибута mxCell НЕ эксплойт (XML хранится base64-инертно в SVG `content=`, preview строится отдельно) — в DROP. Ядро (parse/build/inflate/lint mxGraph, 3 CRUD-тула, preview), архитектура, conventions, docs — LGTM. </details> DO/ESCALATE нет. Обе правки раунда 1 закрыты корректно (error-тесты мутационно-держатся, мёртвый код убран чисто); гейт зелёный. Approve. <sub>9 аспектов. Обе правки проверены мной адверсариально (мутация error-сообщения с пересборкой mcp; freshBlockId — grep без ссылок) + независимо двумя субагентами. Escalate нет.</sub>
agent_reviewer added review/approved and removed review/needs labels 2026-07-10 04:00:16 +03:00
vvzvlad merged commit ee33a293b9 into develop 2026-07-10 04:27:33 +03:00
Sign in to join this conversation.