/security-and-trust-boundaries
Use when writing or reviewing code that parses user input, builds SQL/shell commands, handles secrets/credentials, hashes passwords, changes auth checks, deserializes untrusted data, or constructs paths/URLs from input
$ npx -y skills add oribarilan/97 --skill security-and-trust-boundaries --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.
- You can call itInvoke it directly when you want it.
- Slash command
/security-and-trust-boundaries
Context preview
The summary Claude sees to decide when to auto-load this skill.
Use when writing or reviewing code that parses user input, builds SQL/shell commands, handles secrets/credentials, hashes passwords, changes auth checks, deserializes untrusted data, or constructs paths/URLs from input
SKILL.md
security-and-trust-boundaries.SKILL.mdname: security-and-trust-boundaries
description: Use when writing or reviewing code that parses user input, builds SQL/shell commands, handles secrets/credentials, hashes passwords, changes auth checks, deserializes untrusted data, or constructs paths/URLs from input
Security and Trust Boundaries
Overview
Common security mistakes grouped by trust boundary: input concatenated into a query, a token logged "for debugging", an unguarded endpoint, `pickle.loads` on untrusted bytes. **When code crosses a trust boundary, stop and run the matching checks before you commit.**
This is a **rigid** skill. Jump to the sub-section that matches what you're writing and run that sub-section's checks.
These checks matter most when untrusted input is crossing into a system with real users — production endpoint, shared service, anything that touches user data, secrets, or auth state. In MVPs, prototypes, internal dev tools, and one-off scripts, prefer the simplest thing that works and re-invoke this skill before the code reaches users. **Three rules apply at every stage, even prototypes: no committed credentials in source, no string-built SQL or shell commands, no `pickle.loads` (or equivalent) on untrusted input. Surface these in your summary to the user even in throwaway code.**
The rules below describe properties of code that crosses a trust boundary, whether you authored that code or encountered it in a file you are touching. When you find an issue in pre-existing code adjacent to your edit, surface it in your summary to the user — don't silently rewrite the file outside the scope you were asked to change.
When to invoke
Invoke when you're about to:
- Parse, validate, or transform input that originated outside your process
- Write or execute SQL, NoSQL, LDAP, OS command, shell command, or any other interpreted query/script string
- Handle secrets, tokens, credentials, API keys, certificates, or any session/auth material
- Hash a password, encrypt data, generate a token, or pick a random value used in a security context
- Add, remove, or change an authentication or authorization check, or expose a new endpoint
- Deserialize data (`pickle`, `yaml.load`, Java/PHP unserialize, XML with entities, JSON merge)
- Construct a file path or URL from user-controlled input
- Review or audit code that crosses trust boundaries
Non-triggers — do NOT invoke for
- Renaming a local variable inside a function that happens to live in `auth/` or `crypto/`
- Adjusting a docstring or formatting in security-adjacent code
- A unit test that pins down already-agreed behavior on validated inputs
- Editing config files where the values are not secrets and the keys are not new auth toggles
- An early-stage MVP or prototype where the architecture is still in flux and no real user data is involved
- An internal dev tool, debugging endpoint, or one-off script
- Throwaway code expected to be replaced before reaching users
If the change touches one of these domains even slightly, **invoke anyway** — the per-domain check is short and the bugs are not.
Precedence
- `97/correctness-traps` overlaps on input validation as error handling. **Rule:** trust-boundary crossings (untrusted input, secrets, auth, deserialization, code-execution surfaces) → this skill; non-security correctness (errors, floats, concurrency, IPC, perf, singletons) → that skill. When both clearly apply (e.g., parsing a config file from a possibly-malicious source), run this one first.
Checks by domain
Injection
1. **Parameterize, never concatenate.** SQL: use bound parameters (`cursor.execute("SELECT * FROM u WHERE id = ?", (uid,))`), not f-strings or `+`. NoSQL: use the driver's typed query API, not string-templated JSON. LDAP: escape per RFC 4515 with the driver's helper, not by hand. Command: pass an argv list, not a shell string. Example: `cursor.execute(f"SELECT * FROM users WHERE name = '{name}'")` is exploitable by any user setting their name to `' OR '1'='1`. 2. **`shell=False` is the default; `shell=True` is a vulnerability.** Use `subprocess.run(["git", "clone", url], shell=False)` (argv form), not `subprocess.run(f"git clone {url}", shell=True)`. The argv form passes arguments straight to the kernel; the shell form runs a shell first, which expands `$(...)`, backticks, `;`, `|`, `&&`, globs, and substitutions in attacker-controlled strings. If a shell genuinely is required (rare), every interpolated value must be passed through the language's shell-quoting helper (`shlex.quote`, etc.) — and you should re-justify why a shell is required. 3. **Template engines auto-escape; raw concatenation does not.** Building HTML, SQL, JSON, or any structured output by `+` or f-string puts the structure-vs-data decision on the developer. Use a template engine with auto-escaping on (Jinja2, ERB with safe defaults, parameterized JSON builders), or generate via the language's typed AST (LXML for XML, the JSON library for JSON). The legitimate exception is templating a known-constant string, never user input.
Untrusted-input boundaries
4. **Path traversal: validate to a known root.** `open(os.path.join(BASE, user_filename))` is a vulnerability if `user_filename` can be `../../etc/passwd`. Resolve to a real path and verify the result starts with the intended root (`os.path.realpath(p).startswith(os.path.realpath(BASE) + os.sep)`). Reject `..`, absolute paths, null bytes, alternate path separators, and Windows device names (`CON`, `NUL`, `AUX`). 5. **SSRF: don't fetch arbitrary URLs from input.** Server-side `requests.get(user_url)` lets the attacker pivot into your VPC, hit metadata services (`169.254.169.254`), and read internal endpoints. If you must fetch user-supplied URLs, allowlist the scheme and host (or DNS-resolve and reject private/loopback/link-local ranges) and disable redirects (`allow_redirects=False`). 6. **Deserialization: only on trusted sources, only with a safe loader.** `pickle.loads`, `yaml.load` (without `Sa
Read more
name: security-and-trust-boundaries description: Use when writing or reviewing code that parses user input, builds SQL/shell commands, handles secrets/credentials, hashes passwords, changes auth checks, deserializes untrusted data, or constructs paths/URLs from input
Security and Trust Boundaries
Overview
Common security mistakes grouped by trust boundary: input concatenated into a query, a token logged "for debugging", an unguarded endpoint, `pickle.loads` on untrusted bytes. **When code crosses a trust boundary, stop and run the matching checks before you commit.**
This is a **rigid** skill. Jump to the sub-section that matches what you're writing and run that sub-section's checks.
These checks matter most when untrusted input is crossing into a system with real users — production endpoint, shared service, anything that touches user data, secrets, or auth state. In MVPs, prototypes, internal dev tools, and one-off scripts, prefer the simplest thing that works and re-invoke this skill before the code reaches users. **Three rules apply at every stage, even prototypes: no committed credentials in source, no string-built SQL or shell commands, no `pickle.loads` (or equivalent) on untrusted input. Surface these in your summary to the user even in throwaway code.**
The rules below describe properties of code that crosses a trust boundary, whether you authored that code or encountered it in a file you are touching. When you find an issue in pre-existing code adjacent to your edit, surface it in your summary to the user — don't silently rewrite the file outside the scope you were asked to change.
When to invoke
Invoke when you're about to:
- Parse, validate, or transform input that originated outside your process
- Write or execute SQL, NoSQL, LDAP, OS command, shell command, or any other interpreted query/script string
- Handle secrets, tokens, credentials, API keys, certificates, or any session/auth material
- Hash a password, encrypt data, generate a token, or pick a random value used in a security context
- Add, remove, or change an authentication or authorization check, or expose a new endpoint
- Deserialize data (`pickle`, `yaml.load`, Java/PHP unserialize, XML with entities, JSON merge)
- Construct a file path or URL from user-controlled input
- Review or audit code that crosses trust boundaries
Non-triggers — do NOT invoke for
- Renaming a local variable inside a function that happens to live in `auth/` or `crypto/`
- Adjusting a docstring or formatting in security-adjacent code
- A unit test that pins down already-agreed behavior on validated inputs
- Editing config files where the values are not secrets and the keys are not new auth toggles
- An early-stage MVP or prototype where the architecture is still in flux and no real user data is involved
- An internal dev tool, debugging endpoint, or one-off script
- Throwaway code expected to be replaced before reaching users
If the change touches one of these domains even slightly, **invoke anyway** — the per-domain check is short and the bugs are not.
Precedence
- `97/correctness-traps` overlaps on input validation as error handling. **Rule:** trust-boundary crossings (untrusted input, secrets, auth, deserialization, code-execution surfaces) → this skill; non-security correctness (errors, floats, concurrency, IPC, perf, singletons) → that skill. When both clearly apply (e.g., parsing a config file from a possibly-malicious source), run this one first.
Checks by domain
Injection
1. **Parameterize, never concatenate.** SQL: use bound parameters (`cursor.execute("SELECT * FROM u WHERE id = ?", (uid,))`), not f-strings or `+`. NoSQL: use the driver's typed query API, not string-templated JSON. LDAP: escape per RFC 4515 with the driver's helper, not by hand. Command: pass an argv list, not a shell string. Example: `cursor.execute(f"SELECT * FROM users WHERE name = '{name}'")` is exploitable by any user setting their name to `' OR '1'='1`. 2. **`shell=False` is the default; `shell=True` is a vulnerability.** Use `subprocess.run(["git", "clone", url], shell=False)` (argv form), not `subprocess.run(f"git clone {url}", shell=True)`. The argv form passes arguments straight to the kernel; the shell form runs a shell first, which expands `$(...)`, backticks, `;`, `|`, `&&`, globs, and substitutions in attacker-controlled strings. If a shell genuinely is required (rare), every interpolated value must be passed through the language's shell-quoting helper (`shlex.quote`, etc.) — and you should re-justify why a shell is required. 3. **Template engines auto-escape; raw concatenation does not.** Building HTML, SQL, JSON, or any structured output by `+` or f-string puts the structure-vs-data decision on the developer. Use a template engine with auto-escaping on (Jinja2, ERB with safe defaults, parameterized JSON builders), or generate via the language's typed AST (LXML for XML, the JSON library for JSON). The legitimate exception is templating a known-constant string, never user input.
Untrusted-input boundaries
4. **Path traversal: validate to a known root.** `open(os.path.join(BASE, user_filename))` is a vulnerability if `user_filename` can be `../../etc/passwd`. Resolve to a real path and verify the result starts with the intended root (`os.path.realpath(p).startswith(os.path.realpath(BASE) + os.sep)`). Reject `..`, absolute paths, null bytes, alternate path separators, and Windows device names (`CON`, `NUL`, `AUX`). 5. **SSRF: don't fetch arbitrary URLs from input.** Server-side `requests.get(user_url)` lets the attacker pivot into your VPC, hit metadata services (`169.254.169.254`), and read internal endpoints. If you must fetch user-supplied URLs, allowlist the scheme and host (or DNS-resolve and reject private/loopback/link-local ranges) and disable redirects (`allow_redirects=False`). 6. **Deserialization: only on trusted sources, only with a safe loader.** `pickle.loads`, `yaml.load` (without `Sa
Showing the first part of this file.
Agent skills distilled from the hard-won lessons of world-renowned programmers, in the spirit of "97 Things Every Programmer Should Know"
Repo: oribarilan/97
Other skills on 97.
- /api-design
Use when designing or reviewing a public API, exported function signature, module boundary, exported type/interface, or any contract other code depends on
Open skill - /before-you-refactor
Use when considering, evaluating, or performing a refactor, restructure, cross-file rename, or cleanup
Open skill - /build-deploy-and-tooling
Use when writing, reviewing, or changing build scripts, CI workflows, deploy pipelines, repo setup, or evaluating a new tool/dependency
Open skill - /clean-code
Use when writing or reviewing functions, classes, naming, or non-trivial logic (≥3 lines)
Open skill - /correctness-traps
Use when writing or reviewing error handling, floating-point math, concurrent code, remote calls, singletons/globals, hot-path data structures, or high-volume log statements
Open skill - /domain-modeling
Use when introducing, reviewing, or renaming a top-level type, table, or domain concept; or choosing where state lives (in-memory vs persistent)
Open skill

