secure-llm-features
Skill7 starsChanged 16 days agoMITView on GitHub
Use when shipping or reviewing an LLM feature that reads untrusted text or has tools — prompt-injection threat model, least-privilege tools scoped by the user's token, safe output handling, per-user rate and cost limits, logs without prompt PII; mapped to the OWASP LLM Top 10 (2026).
1 other project uses a version of this.
Adopted by
Most recently taken up by
- VelimirMueller/ai-development-skillsword for word, first seen Oct 25, 2026
Other versions1 project uses a version of this.
Word for word · 1
- VelimirMueller/ai-development-skills★ 7100% same
Edited · 0
None found.
Wrote their own version · 0
None found.
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository156 other files it gives its agents.
- skills/swift/swiftui-design-principles/AGENTS.mdAGENTS.md
- skills/swift/ios-code-audit/CLAUDE.mdCLAUDE.md
- skills/swift/swift-focusengine-pro/llms.txtllms.txt
- build-llm-seamSkill
- build-mcp-serverSkill
- build-rag-pipelineSkill
- set-up-llm-evalsSkill
- build-nextjs-backendSkill
- build-supabase-edge-functionSkill
- configure-backend-testsSkill
- design-http-apiSkill
- harden-backendSkill
- scaffold-fastapi-serviceSkill
- scaffold-go-serviceSkill
- scaffold-hono-serviceSkill
- secure-supabase-rlsSkill
- set-up-backend-authSkill
- set-up-background-jobsSkill
- set-up-databaseSkill
- set-up-nextjs-supabase-authSkill
- set-up-observabilitySkill
- set-up-supabaseSkill
- build-cliSkill
- release-cliSkill
- set-up-dev-toolchainSkill
- audit-securitySkill
- audit-toolchainSkill
- extend-skillsetSkill
- promote-releaseSkill
- set-up-stack-profileSkill
- write-commit-messagesSkill
- write-pull-requestsSkill
- analyze-complexitySkill
- apply-algorithmsSkill
- apply-design-patternsSkill
- apply-design-principlesSkill
- apply-solidSkill
- choose-data-structureSkill
- debug-systematicallySkill
- enforce-dependency-ruleSkill
- refactor-safelySkill
- slice-and-estimateSkill
- write-clean-testsSkill
- write-readable-codeSkill
- clean-frontend-scaffoldingSkill
- configure-accessibilitySkill
- configure-analyticsSkill
- configure-ciSkill
- configure-error-trackingSkill
- configure-lintingSkill
- configure-test-stackSkill
- configure-typescriptSkill
- create-moduleSkill
- optimize-performanceSkill
- scaffold-frontend-projectSkill
- set-up-authSkill
- set-up-design-systemSkill
- set-up-document-headSkill
- set-up-error-boundariesSkill
- set-up-feature-flagsSkill
- The first 60 shown.
The file
We split this skill into the rules it gives an agent
5 rules. About 0 are common, 1 is distinctive, and 4 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
4
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 12
Read .claude/stack-profile.md (languages, backend.track, frontend, ai).
- Very specific: line 34
No per-user budget or rate limit, or a call without max_tokens → budget guard.
- Distinctive: line 36
No injection case in the eval set → add them (set-up-llm-evals).
- Very specific: line 37
All present and Verify passes → "already in place".
- Very specific: line 72
Run sanitizeModelMarkdown(text, ALLOWED_IMAGE_HOSTS) before rendering, then render with a renderer that escapes HTML.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Secure LLM Features
A model cannot tell instructions from data, so any text it reads (a document, a web page, a ticket, a tool result) can try to steer it. You cannot prompt that away. You design so that a steered model has no power: its authority is the user's token, its writes need a human, its output is escaped like any untrusted input, and its spend has a ceiling. The contract and the OWASP map are in llm-security.md.
1. Audit current state
Read .claude/stack-profile.md (languages, backend.track, frontend, ai). If absent, detect per stack-profile.md. Change nothing yet; build the picture:
grep -rnE "llm\.(generate|stream)|messages\.(create|stream|parse)|@mcp\.tool|registerTool" src # every model call and tool
grep -rnE "(userId|user_id|tenantId|tenant_id|accountId)" src | grep -iE "tool|schema|z\.object" # identity as a tool input
grep -rnE "dangerouslySetInnerHTML|v-html|innerHTML|marked\(|markdown-it|react-markdown" src # output sinks
grep -rniE "(logger|console)\.[a-z]+\(.*(prompt|messages|completion|content)" src # prompt content in logs
grep -rniE "api[_-]?key|password|secret|bearer|internal only|do not reveal" prompts # secrets or policy in prompts
grep -rnE "maxTokens|max_tokens" src | grep -v platform/llm # per-call limits
grep -rnE "eval\(|new Function|child_process|exec\(|subprocess|\$queryRawUnsafe|sql\.raw" src # dangerous sinks fed by textDraw the threat model in a table: assets (whose data), untrusted text sources (documents, tickets, web, user), tools with their effect (read or write) and scope, sinks (browser, terminal, SQL, shell, another API). Every row without a control is a finding.
2. Decide what to do
Fix in this order; stop when the audit is clean:
- A tool takes identity from input, or runs with a service credential instead of the user's → bind tools to the principal (step 5).
- Retrieval without a tenant filter in SQL and RLS → build-rag-pipeline.
- A write tool without a human confirmation → proposal and confirm.
- Model output reaches a sink unescaped → sanitize.
- No per-user budget or rate limit, or a call without
max_tokens→ budget guard. - Prompts or completions in logs or span attributes → remove.
- No injection case in the eval set → add them (set-up-llm-evals).
- All present and
Verifypasses → "already in place".
3. Detect the track
| Signal | Branch |
| Chat or Q&A, no tools | Output handling, budgets, logging. Skip tool binding |
| RAG | Add tenant isolation and the delimiter rules; the answer step gets no tools |
| Agent with tools | Everything; plus a step cap and the reader/actor split (security-patterns.md) |
| You publish an MCP server | build-mcp-server: OAuth audience, scopes, confirmation by elicitation |
| You consume third-party MCP servers | Pin versions, review tool descriptions on every update, scope what they may reach |
| Browser or terminal renders the output | Sinks table in security-patterns.md |
4. Install only what's missing
Nothing beyond the seam (build-llm-seam) and Zod. If you render markdown, use a renderer that does not emit raw HTML (markdown-it 15 with its default html: false, react-markdown 10) or sanitize the HTML with DOMPurify 3; checked 2026-10-09 that markdown-it escapes <script> and blocks javascript: links but does render a remote image, which is the exfiltration case the helper below removes.
5. Generate the seams
Code: security-code.md.
src/platform/llm-security/output.ts # sanitizeModelMarkdown: HTML, images, links, control sequences
src/platform/llm-security/tools.ts # Principal, defineTool, bindTools: scopes, no identity input, write proposals
src/platform/llm-security/budget.ts # userBudget guard, BudgetStore
src/platform/llm-security/budget-pg.ts # Postgres store (migrations/0002_llm_usage.sql)Edit: the image host allow-list (empty by default: every image is removed), the daily limit per user, scope names, the worst-case cost estimate per task.
6. Wire it up
- Build a
Principalfrom the verified session or token in the request handler (user, tenant, scopes). Pass it down; never derive it from request body or model output. - Give the model
bindTools(principal, tools).descriptorsand execute its calls through.call(name, input). Cap the loop at 10 steps. Aneeds_confirmationresult goes to your UI as a confirmation dialog; only the user's click calls.confirm(actionId). - Wrap each model call in
budget.guard(principal.userId, worstCaseUsd, () => llm.generate(…)). MapBudgetExceededto429withRetry-After. Add a per-minute rate limit at the gateway or in middleware (see the status-code guidance in api-contract-patterns.md). - Run
sanitizeModelMarkdown(text, ALLOWED_IMAGE_HOSTS)before rendering, then render with a renderer that escapes HTML. Terminal and IDE sinks: strip control sequences (the same function does). - Logs and spans carry ids, task, tokens, cost, error kind. The seam does this already; delete any logging of prompts, messages or completions.
7. Verify
pnpm vitest run tests/security.test.ts # sanitizer, tool binding, confirmation, budget
grep -rniE "(logger|console)\.[a-z]+\(.*(prompt|messages|completion)" src # expect: no outputExpected: tests pass: a tool call that names another user is ignored, a user without the scope never sees the tool, a write returns needs_confirmation and only the same user can confirm it, an exfiltration image is removed, a second call past the daily limit throws BudgetExceeded.
Then attack your own feature with the cases in security-patterns.md (instruction in a document, markdown-image exfiltration, a request for another tenant's data, a prompt-extraction attempt) and assert on effects: no tool call happened, no image URL came out, no other tenant's text appeared. Put them in the eval gate.
References
- security-patterns.md: rules and why. security-code.md: code.
- llm-security.md: trust boundaries, OWASP LLM Top 10 (2026) map, merge checklist.
- security-baseline.md, logging-contract.md.
- stack-versions.md.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

