Add design/accessibility/security/sanitization skills
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
---
|
||||
name: code-sanitization
|
||||
description: Use when writing any code that handles external input (user input, API responses, file uploads, database queries, shell commands). Framework-agnostic injection-prevention baseline.
|
||||
---
|
||||
|
||||
# Code Sanitization
|
||||
|
||||
Framework-agnostic baseline for handling untrusted input safely. Applies
|
||||
regardless of language — the specific function names differ, the
|
||||
principle doesn't.
|
||||
|
||||
## The core rule: sanitize on input, escape on output, separately
|
||||
|
||||
These are two different concerns, both required:
|
||||
- **Sanitize/validate on input**: reject or normalize data that doesn't
|
||||
match the expected shape, as early as possible (at the point you first
|
||||
receive it).
|
||||
- **Escape on output**: transform data for the specific context it's
|
||||
being placed into (HTML, a SQL query, a shell command, a URL, JSON) —
|
||||
every different output context needs its OWN escaping, applied at the
|
||||
point of output, not once globally.
|
||||
|
||||
A common real mistake: sanitizing once on input and assuming that's
|
||||
sufficient for every later output context. It isn't — data that's safe
|
||||
to store isn't automatically safe to interpolate into HTML, and separately
|
||||
isn't automatically safe to interpolate into a shell command.
|
||||
|
||||
## SQL injection
|
||||
|
||||
**Parameterized queries / prepared statements, always, no exceptions.**
|
||||
Never build a SQL string via concatenation or interpolation with a
|
||||
variable in it, even if you're "sure" the variable is safe (a value that
|
||||
was safe when the code was written is not guaranteed to stay safe as
|
||||
the codebase evolves and new call sites appear).
|
||||
|
||||
## Cross-site scripting (XSS)
|
||||
|
||||
Escape for the exact context:
|
||||
- HTML body text: HTML-entity-escape (`<`, `>`, `&`, `"`, `'`).
|
||||
- HTML attribute: attribute-escape (stricter than body-text escaping).
|
||||
- JavaScript string embedded in a `<script>` block: JS-string-escape (NOT
|
||||
the same as HTML-escaping — a common real mistake).
|
||||
- URL parameter: URL-encode.
|
||||
Use the templating/framework's built-in auto-escaping wherever available
|
||||
rather than hand-rolling escaping — auto-escaping systems have already
|
||||
solved the context-detection problem correctly; hand-rolled escaping is
|
||||
an easy place to get a context wrong.
|
||||
|
||||
## Command injection
|
||||
|
||||
Never build a shell command string by concatenating untrusted input. Use
|
||||
an API that passes arguments as a real array/list (avoiding shell
|
||||
interpretation entirely) rather than a single command string, wherever
|
||||
the language/runtime offers that option. If a single command string is
|
||||
genuinely unavoidable, every untrusted component needs proper shell-
|
||||
escaping for the target shell — and even then, prefer the array-argument
|
||||
API if it exists.
|
||||
|
||||
See the `remote-shell-quoting-safety` skill for the specific, separate
|
||||
problem of content getting corrupted (not maliciously, just broken) when
|
||||
passing through multiple nested shell layers — a different concern from
|
||||
injection, but often encountered in the same code paths.
|
||||
|
||||
## Path traversal
|
||||
|
||||
Never build a filesystem path by directly concatenating user input.
|
||||
Validate the resolved absolute path stays within the intended base
|
||||
directory (resolve `..`/symlinks and check the result, don't just
|
||||
regex-reject `..` in the raw input — that's bypassable). Prefer an
|
||||
allowlist of permitted filenames/paths over trying to blocklist
|
||||
dangerous patterns.
|
||||
|
||||
## File uploads
|
||||
|
||||
- Validate actual file content/type (magic bytes), not just the
|
||||
extension or the client-supplied MIME type — both are trivially
|
||||
spoofable.
|
||||
- Store uploads outside the web-servable document root, or with
|
||||
execution disabled for that directory, so an uploaded file can never
|
||||
be directly requested and executed as code even if a validation gap
|
||||
lets something malicious through.
|
||||
- Enforce a size limit server-side, not just client-side.
|
||||
|
||||
## Deserialization
|
||||
|
||||
Never deserialize untrusted data with a format/library that can
|
||||
instantiate arbitrary objects or execute code as a side effect of
|
||||
deserializing (e.g. PHP's `unserialize()` on untrusted input, Python's
|
||||
`pickle.loads()` on untrusted input). Use a safe, data-only format (JSON)
|
||||
for anything touching untrusted input.
|
||||
|
||||
## General principle
|
||||
|
||||
When in doubt about whether input is "trusted," treat it as untrusted.
|
||||
Data crossing any trust boundary (a network request, a file upload, a
|
||||
database read of data that was itself written by a less-trusted
|
||||
component, an environment variable in a multi-tenant context) should be
|
||||
validated/escaped as if it were directly attacker-controlled, because in
|
||||
a surprising number of real incidents, eventually it was.
|
||||
Reference in New Issue
Block a user