T09 · Insecure Skill Coding Practices
- Location
scripts/ontology.py:120- Finding
Graph Mutations Bypass Schema Validation Before Persistence
- Content
View full analysis
Vulnerability Details
File Location:
scripts/ontology.py:120-134,scripts/ontology.py:174-182,scripts/ontology.py:201-212, andscripts/ontology.py:250-279
Vulnerability Type: Missing pre-commit validation
Risk Level: MediumThe documented security contract states in
SKILL.md:12:text Every mutation is validated against type constraints before committing.However, entity creation directly appends an unvalidated operation:
python def create_entity(type_name: str, properties: dict, graph_path: str, entity_id: str = None) -> dict: """Create a new entity.""" entity_id = entity_id or generate_id(type_name) timestamp = datetime.now(timezone.utc).isoformat() entity = { "id": entity_id, "type": type_name, "properties": properties, "created": timestamp, "updated": timestamp } record = {"op": "create", "entity": entity, "timestamp": timestamp} append_op(graph_path, record) return entityUpdates are also committed without validating the resulting entity:
python def update_entity(entity_id: str, properties: dict, graph_path: str) -> dict | None: """Update entity properties.""" entities, _ = load_graph(graph_path) if entity_id not in entities: return None timestamp = datetime.now(timezone.utc).isoformat() record = {"op": "update", "id": entity_id, "properties": properties, "timestamp": timestamp} append_op(graph_path, record) entities[entity_id]["properties"].update(properties) entities[entity_id]["updated"] = timestamp return entities[entity_id]Relations are likewise persisted without checking endpoint existence, allowed types, cardinality, or acyclicity:
python def create_relation(from_id: str, rel_type: str, to_id: str, properties: dict, graph_path: str): """Create a relation between entities.""" timestamp = datetime.now(timezone.utc).isoformat() record = { ...[truncated 4064 chars]- Remediation
View remediation
Remediation Suggestions
-
Validate every proposed mutation before calling
append_op():- For creation, validate the complete proposed entity against its type schema.
- For updates, merge the proposed properties into a copy of the current entity and validate the resulting entity.
- For relations, validate endpoint existence, endpoint types, cardinality, and acyclicity against the graph state that would result from the operation.
-
Pass the schema path into mutation functions or introduce a graph-store class that owns both graph and schema configuration. Do not rely on callers to run a separate validation command.
-
Reject forbidden secret-bearing properties before persistence. For
Credential, explicitly denypassword,secret,token,key, andapi_key, and require a validsecret_ref. -
Ensure atomic behavior: validation must complete successfully before the append occurs. Return a nonzero CLI exit status and a clear error when a mutation violates the schema.
-
Consider defensive secret-pattern checks in addition to property-name restrictions, because callers may place sensitive values under unexpected property names.
-
Add regression tests covering:
- Missing required fields.
- Forbidden Credential fields.
- Invalid enum values.
- Relations with missing endpoints.
- Disallowed relation endpoint types.
- Cardinality violations.
- Cyclic relations marked as acyclic.
- Updates that turn a previously valid entity into an invalid one.
-
Provide a remediation or compaction procedure for existing invalid records. Since the graph is append-only, merely appending an update does not erase a secret from historical storage; exposed secrets should be rotated and the graph securely rewritten where appropriate.
-
