You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
update_schema deletes Core/System-protected schema fields with no protection-level check (guard bypass) #2194
handle_update_schema's remove_fields removes any field purely by name (fields.retain(|f| !remove_names.contains(&f.name))) with no SchemaProtectionLevel check, so an update_schema call can permanently delete a Core- or System-protected field that the UI and the can_delete_field guard forbid removing.
Failure scenario
On any install the built-in task schema is seeded with a status field that is SchemaProtectionLevel::Core, required:true, indexed, enum (core_schemas.rs:60-101); can_delete_field("status") returns false by design (unit-tested at schema_node.rs:563). The Svelte UI never exposes it (task-schema-form.svelte:121 filters out core/system fields). But calling the agent tool update_schema (agent/src/local_agent/tools.rs:2956 -> handle_update_schema), the gRPC UpdateSchema handler (daemon/src/services/node_service.rs:1303), or the CLI schema update with {"schema_id":"task","remove_fields":["status"]} runs the retain at schema/mod.rs:816 with no protection check. validate_schema_node (behaviors/mod.rs:1415) only checks name-uniqueness and title_template tokens (task has title_template:None), so it passes, and update_node_unchecked persists the mutilated schema. Because seed_core_schemas_if_needed (node_service/mod.rs:1344-1366) only recreates a schema whose id is entirely absent, the deleted field is never restored on reboot. Result: the Core-protected required status field is permanently gone from the task type, breaking status-enum validation and the board/status UI that read the schema field definition, and violating the documented+tested invariant that only User fields are deletable. can_delete_field/can_modify_field (schema_node.rs:316,326) exist for exactly this but are never called on any non-test path.
Location
packages/core/src/schema/mod.rs:816
Severity
important
Found via a full-repo adversarial bug sweep, independently verified by two separate skeptical reviewers reading the actual current code before filing.
Fixed in #2304: added a protection-level guard (checked against SchemaProtectionLevel via can_delete_field/can_modify_field) to handle_update_schema's Phase 0 and to the NodeService::rename_schema_field/update_schema_field_friendly_name primitives, so a Core/System-protected field can no longer be removed, renamed, or relabeled through update_schema. Also moved rename-destination grammar validation ahead of the migration/persist step, so a malformed destination name is rejected before anything is written rather than after. Regression tests cover the removal, rename, friendly-name-relabel, and grammar-before-persist scenarios described here. PR is open for review, not yet merged.
Activity
mstomar125 commentedon Aug 27, 2026
Fixed in #2304: added a protection-level guard (checked against
SchemaProtectionLevelviacan_delete_field/can_modify_field) tohandle_update_schema's Phase 0 and to theNodeService::rename_schema_field/update_schema_field_friendly_nameprimitives, so a Core/System-protected field can no longer be removed, renamed, or relabeled throughupdate_schema. Also moved rename-destination grammar validation ahead of the migration/persist step, so a malformed destination name is rejected before anything is written rather than after. Regression tests cover the removal, rename, friendly-name-relabel, and grammar-before-persist scenarios described here. PR is open for review, not yet merged.