T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/generate_tests.py:173
- Finding
- Read-only mode can generate helpers that issue DELETE requests<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate_tests.py:173-181`, `scripts/generate_tests.py:436-438` **Vulnerability Type**: Improper enforcement of read-only operation **Risk Level**: High ### Vulnerable Code ```python try: # CREATE total += 1 if self.test_create(): passed += 1 {test_sequence} finally: # Always try to delete (rollback) self.test_delete() ``` The generator changes the initial operation to GET in read-only mode but does not remove the unconditional deletion: ```python if read_only: create_method = 'GET' create_endpoint = feature_config.get('read', {}).get('detail_endpoint') or feature_config.get('read', {}).get('endpoint', create_endpoint) test_sequence = '' ``` The generated deletion method can issue a state-changing request: ```python if "{delete_request_encoding}" == "json": r = requests.{http_delete_method}( f"{API_BASE}{endpoint}", json=payload, headers=self.get_auth_headers(), timeout=10 ) elif "{delete_request_encoding}" == "params": r = requests.{http_delete_method}( f"{API_BASE}{endpoint}", params=payload, headers=self.get_auth_headers(), timeout=10 ) else: r = requests.{http_delete_method}( f"{API_BASE}{endpoint}", data=payload, headers=self.get_auth_headers(), timeout=10 ) ``` ### Technical Analysis The `--read-only` option does not enforce an invariant that all generated requests are non-mutating. It changes the nominal CREATE operation to GET and removes the update sequence, but `run()` still executes `self.test_delete()` in its `finally` block. If the GET response supplies a value through the configured ID extraction path, that value is assigned to `self.resource_id`. The cleanup routine then treats the retrieved resource as test-created data and sends the configured deletion request. The production-like URL confirmation also excludes ...[truncated 1370 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Generate no DELETE method and make no call to `test_delete()` when `read_only` is true. 2. Use separate read-only and write-capable templates rather than conditionally modifying one CRUD template. 3. Enforce a strict read-only method allowlist containing only `GET` and `HEAD`. 4. Reject configurations containing create, update, or delete operations when `--read-only` is selected. 5. Do not assign IDs returned by read operations to cleanup state. 6. Require explicit confirmation for every production-like destination, including read-only mode. 7. Add automated tests that inspect generated helpers and fail if read-only output contains `post`, `put`, `patch`, `delete`, or other mutation-capable request calls. ]]>
