Skip to content

v2.39.4 — audit patch: security hardening + data-safety guards - #22

Merged
francoios merged 3 commits into
mainfrom
Development-Fable-Improvements
Jul 24, 2026
Merged

v2.39.4 — audit patch: security hardening + data-safety guards#22
francoios merged 3 commits into
mainfrom
Development-Fable-Improvements

Conversation

@francoios

Copy link
Copy Markdown
Contributor

Patch release from a 43-run audit (33 dimensions + 10 blind-spot dimensions, 168 agents, every CRITICAL/HIGH adversarially verified). Companion server: 2.35.5.

The audit's own verdict on the codebase: fundamentals are strong, but "a correct pattern gets built once and then not propagated, and safety mechanisms fail OPEN instead of closed." Almost every fix below is "the correct code already existed elsewhere in the same file."

🔒 Security

  • Arbitrary .md file read over an unauthenticated GETMCPContextManager.GetContextByCategory fed the raw URL segment into Path.Combine, so ..%2f traversal (or an absolute path, which replaces the folder outright) escaped the context folder. Category names are now allowlisted. Live-verified: six traversal/absolute/NUL payloads blocked, while plain, Custom/-prefixed and bare-name lookups still resolve.
  • Browser-reachable dispatch closed — the Origin check only ran when an Origin was present, but a no-cors subresource load (<img>, <iframe>) sends none, so a page a developer merely visited could reach the bridge. Requests carrying browser Sec-Fetch-* metadata are refused (page script cannot forge those headers), and every non-read-only route now requires POST.
  • Inbound request bodies bounded (32 MB) — the read was an unbounded ReadToEnd; one local request could OOM the editor.

🛡️ Data safety

  • asset/delete was silent, permanent and unconfined. Now path-confined, refuses a FOLDER unless recursive:true (disclosing the contained asset count), and defaults to the OS trash — asset deletion registers no undo, so unity_undo_last could never recover it.
  • scene/new replaced the current scene with no dirty check while scene/open twenty lines above had one. Both now refuse when ANY loaded scene is dirty unless saveFirst / discardUnsavedChanges.
  • Three modal dialogs removed from the request pump — each blocks the editor on a human click and blocks forever unattended. Includes one in the new guard's own saveFirst path, caught by the follow-up audit reviewing code added minutes earlier in this same wave.
  • asset/create-prefab got the overwrite guard create-material already had (overwriting kept the .meta GUID, silently re-binding every scene reference).

⚙️ Reliability

  • The bridge no longer unregisters from the instance registry on domain reload — that entry is exactly what the server's compile-resilience relies on, so a routine recompile looked like the project had gone away.
  • ProBuilder versionDefine 4.0.0 → 5.1.0MakeUnique() only exists from 5.1.0, so ProBuilder 4.x–5.0.x failed to compile the entire assembly: a dead plugin, not a degraded feature.
  • License metadata corrected — declared MIT while shipping the AnkleBreaker Open License v1.0.

Gates

0 Unity compile errors (verified against a live 6000.3.9f1 editor), companion server 60/60 tests, route registry 337 in sync, guards verified end-to-end through the real MCP.

Full detail: CHANGELOG.md.

…xtByCategory fed the raw URL segment into Path.Combine, so ..%2f traversal (or an absolute path, which replaces the folder outright) escaped Assets/MCP/Context. Category names are now allowlisted to a plain filename optionally under Custom/. Live-verified: 6 traversal/absolute/NUL payloads blocked, plain + Custom/ + bare-name lookups still resolve

[FIX] Bridge, browser-reachable dispatch : the Origin check only ran when an Origin was present, but a no-cors subresource load (img/iframe) sends none - a visited page could reach the bridge. Refuse requests carrying browser Sec-Fetch-Site/Mode metadata (page script cannot forge them) and require POST on every non-read-only route

[FIX] Bridge, unbounded inbound body : ReadToEnd had no limit, so one local request could OOM the editor. Cap at 32MB with a 413, including chunked bodies with no declared length

[FIX] Asset, delete was silent + permanent + unconfined : route through MCPAssetSafety, refuse a FOLDER unless recursive:true (disclosing the contained asset count), default to the OS trash (permanent:true keeps the hard delete). Asset deletion registers no undo, so undo/last could never recover it

[FIX] Scene, new discarded unsaved work with no warning : scene/new replaced the current scene unguarded while scene/open 20 lines above had a check. Both now refuse when ANY loaded scene is dirty unless saveFirst/discardUnsavedChanges

[FIX] Scene, modal dialog raised from the request pump : SaveCurrentModifiedScenesIfUserWantsTo blocks the editor on a human click and blocks forever unattended. Decide from the arguments instead

[FIX] Asset, create-prefab missing the overwrite guard create-material already had : overwriting kept the .meta GUID so scene references silently re-bound

[FIX] Bridge, unregistered from the instance registry on every domain reload : that entry is what the server's compile-resilience relies on, so a routine recompile looked like the project had gone away. Stop(false) across reloads

[FIX] ProBuilder versionDefine 4.0.0 -> 5.1.0 : MakeUnique() only exists from 5.1.0, so PB 4.x-5.0.x failed to compile the whole assembly - a dead plugin, not a degraded feature

[CLEAN] License : declared MIT while the shipped LICENSE is the AnkleBreaker Open License v1.0 (attribution required, resale prohibited)
…emove : SaveOpenScenes() on a never-saved scene raises the native Save Scene panel, so the escape hatch reintroduced the pump-blocking dialog. Refuse saveFirst when any dirty scene has no asset path, naming those scenes

[FIX] Scene, save opened the native Save panel on a never-saved scene : scene/save now takes an optional path (also acting as Save-As, confined via MCPAssetSafety) and refuses with requiresPath instead of blocking the pump on a human when neither the scene nor the caller supplies one

Both found by the blind-spot audit reviewing the guard added earlier in this same wave.
…ter the 2.39.4 entry was written (saveFirst re-raising the Save Scene panel, and scene/save gaining an optional path)
@francoios
francoios merged commit 4c03a96 into main Jul 24, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant