T09 · Insecure Skill Coding Practices
- Location
scripts/ontology.py:104- Finding
Graph Mutations Are Persisted Without Pre-Commit Constraint Validation
- Content
View full analysis
Vulnerability Details
File Location:
scripts/ontology.py, lines 104-119
Vulnerability Type: Missing validation before persistent storage
Risk Level: MediumThe skill states that every mutation is validated against type constraints before being committed. However, entity creation immediately appends attacker-controlled properties to the persistent JSONL graph without loading or applying the configured schema.
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 entityThe same unsafe persistence pattern is also present in
update_entityat lines 154-169 andcreate_relationat lines 179-192. Validation is only available as a separatevalidatecommand and therefore occurs after data has already been committed. Moreover, validation only reports errors; it does not roll back or remove invalid records.Technical Analysis
The CLI accepts user-controlled JSON through
--propsand passes it directly to mutation functions.create_entityconstructs a record and invokesappend_opwithout callingload_schema,validate_graph, or any equivalent per-mutation validator.This breaks the documented security boundary for sensitive ontology types. For example, the documented
Credentialschema requires indirect secret references and forbids properties such aspassword,secret,token,key, andapi_key. A caller can nevertheless store those fields directly in the plaintext graph.Relation ...[truncated 2509 chars]
- Remediation
View remediation
Remediation Suggestions
-
Validate every proposed mutation before appending it:
- Load the configured schema.
- Apply the mutation to an in-memory copy of the current graph.
- Run all entity, relation, cardinality, reference, and acyclicity checks.
- Append the operation only if validation succeeds.
-
Add dedicated mutation validators rather than invoking only whole-graph validation:
- Verify required, forbidden, and enum-constrained properties on create and update.
- Reject unknown or malformed entity structures.
- Ensure both relation endpoints exist.
- Enforce relation endpoint types, cardinality, and acyclicity before writing.
-
Install a secure default schema as executable configuration rather than leaving the reference definitions solely in Markdown. Fail closed for sensitive types when the schema is missing or malformed.
-
Explicitly reject secret-bearing property names for
Credentialentities even when no custom schema exists. Consider recursively detecting prohibited fields to prevent secrets from being nested under another object. -
Use an atomic commit process. Write the validated operation to a securely created temporary file, flush it, and atomically replace or append under an appropriate file lock to prevent concurrent check-to-write races.
-
Return a nonzero exit status on rejected mutations and validation failures so calling agents cannot mistake invalid operations for successful commits.
-
Add regression tests covering forbidden credential fields, missing required fields, invalid enum values, nonexistent relation endpoints, type mismatches, cardinality violations, cycles, and concurrent mutations.
-
