T09 · Insecure Skill Coding Practices
- Location
scripts/ontology.py:120- Finding
Schema Constraints Are Not Enforced Before Graph Mutations
- Content
View full analysis
Vulnerability Details
File Location:
scripts/ontology.py:120-137,scripts/ontology.py:163-177,scripts/ontology.py:201-213,scripts/ontology.py:250-383,scripts/ontology.py:507, andscripts/ontology.py:542
Vulnerability Type: Missing validation before persistent graph mutations
Risk Level: MediumVulnerable Code
Entity creation immediately appends the supplied properties to persistent storage:
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 likewise committed without schema validation:
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 also persisted without checking endpoint existence, allowed entity 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 = dat ...[truncated 3368 chars]- Remediation
View remediation
Remediation Suggestions
-
Validate proposed state before every write. Load the current graph and schema, apply the proposed mutation in memory, and run all applicable checks before calling
append_op. -
Reject forbidden sensitive properties at the mutation boundary. For credential-like entities, reject raw passwords, secrets, tokens, private keys, and API keys before they can enter the append-only log. This check should not depend solely on an optional schema file.
-
Validate entity operations comprehensively. Enforce:
- Known entity types where required.
- Required properties.
- Forbidden properties.
- Property types and enumeration values.
- Unique entity IDs.
- Valid date and datetime values.
- Event ordering constraints.
-
Validate relations before persistence. Confirm:
- Both endpoint entities exist.
- Source and destination types are permitted.
- Cardinality remains valid.
- The new edge does not introduce a cycle for acyclic relation types.
- Relation properties satisfy their schema.
-
Implement atomic mutation semantics. For multi-step transformations, validate the complete proposed state and commit it atomically. If the storage design is expanded beyond single append operations, use a temporary file and atomic replacement or a transactional database.
-
Address secrets already written to history. Because the graph is append-only, a logical deletion does not remove historical plaintext. Rotate exposed credentials and compact or securely rewrite the graph to eliminate sensitive historical records.
-
Align documentation with implementation. Either implement the promised pre-commit validation and rollback behavior or clearly document validation as post-hoc only. Preventive enforcement is strongly recommended for forbidden secret properties.
-
Add regression tests. Tests should verify that invalid creates, updates, and relations produce ...[truncated 63 chars]
-
