diff --git a/AGENTS.md b/AGENTS.md index 4c2b3d6c..2820f11b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -142,7 +142,9 @@ def my_view(request, api=False): Django's template engine escapes variables by default. Do **not** use `{% autoescape off %}` or the `|safe` filter on any value derived from scan data, manifests, or user input. When rendering user-controlled strings outside of templates (e.g., in a JSON response -built by hand), use `django.utils.html.escape()` explicitly. +built by hand), use `django.utils.html.escape()` explicitly. Template auto-escaping +does not protect JavaScript DOM sinks: never assign scan, task, or user-derived +strings to `innerHTML`; use `textContent` (see `templates/general/tasks.html`). ### ORM — No Raw SQL @@ -163,7 +165,9 @@ RecentScansDB.objects.raw(f'SELECT * FROM ... WHERE MD5 = "{checksum}"') Django's `CsrfViewMiddleware` is enabled globally. Do not use `@csrf_exempt` on any view that modifies state. API endpoints that accept an `X-Csrftoken` header or use token-based auth are the only legitimate exception, and that pattern is already -established in the existing API views. +established in the existing API views. Mutating actions (including dynamic-analysis +start/stop/stream) must be POST with a CSRF token, not GET. Split GET-render from +POST-stream when a page both renders and then streams (e.g. logcat). ---