From ef17d19a113c2da33243b7536265c432460d613d Mon Sep 17 00:00:00 2001
From: Nikolas de Hor
Date: Tue, 19 May 2026 12:18:46 -0300
Subject: [PATCH 1/2] docs: adicionar marketing pack shieldcode (README, blog,
social)
---
README.md | 4 +++
docs/blog-post-pt.md | 54 ++++++++++++++++++++++++++++++++++++++++
docs/linkedin-post-pt.md | 29 +++++++++++++++++++++
docs/x-thread-pt.md | 20 +++++++++++++++
4 files changed, 107 insertions(+)
create mode 100644 docs/blog-post-pt.md
create mode 100644 docs/linkedin-post-pt.md
create mode 100644 docs/x-thread-pt.md
diff --git a/README.md b/README.md
index ed0d4eb..87b1d38 100644
--- a/README.md
+++ b/README.md
@@ -7,6 +7,10 @@
Security hardening and production-grade error handling expertise for Claude Code
+
+ > **Nota:** ShieldCode é uma skill do Claude Code, não um pacote npm ou pip.
+
+
Install ·
Coverage ·
diff --git a/docs/blog-post-pt.md b/docs/blog-post-pt.md
new file mode 100644
index 0000000..e4f4fc5
--- /dev/null
+++ b/docs/blog-post-pt.md
@@ -0,0 +1,54 @@
+# Código Seguro com Claude Code: Como o ShieldCode protege suas aplicações
+
+O uso de assistentes de inteligência artificial para escrever código tornou-se uma prática comum entre desenvolvedores que buscam produtividade. O Claude Code, em particular, destaca-se pela sua capacidade de operar diretamente no sistema de arquivos e gerenciar o ciclo de vida do desenvolvimento. Entretanto, a velocidade com que a IA produz código muitas vezes mascara um problema crítico: a segurança.
+
+Estudos recentes indicam que uma parcela significativa do código gerado por IA contém vulnerabilidades de segurança. Lacunas no tratamento de erros, falta de validação de inputs e o uso de padrões obsoletos são comuns. Quando a IA prioriza a funcionalidade imediata em detrimento da segurança robusta, ela acaba transferindo uma dívida técnica perigosa para o desenvolvedor humano.
+
+Foi para fechar essa brecha que criei o **ShieldCode**, uma skill para o Claude Code focada em segurança e resiliência de software.
+
+## O problema do código gerado por IA
+
+A IA é treinada em vastos repositórios de código, o que inclui tanto exemplos excelentes quanto códigos legados ou inseguros. Sem uma diretriz clara, é comum que agentes de IA utilizem concatenação direta de strings em consultas SQL, ignorem a validação de tipos em entradas de usuários ou utilizem funções de hash inseguras como MD5 para senhas.
+
+Além disso, o tratamento de erros em código gerado por IA frequentemente deixa a desejar. É comum ver blocos de "catch" vazios ou o retorno de stack traces completos para o cliente final, o que facilita ataques de enumeração e descoberta de infraestrutura por agentes maliciosos.
+
+## O que é o ShieldCode?
+
+Diferente do que muitos podem pensar, o ShieldCode não é uma biblioteca npm ou um pacote pip que você instala no seu projeto. Ele é uma **skill** para o Claude Code. Isso significa que ele atua como um conjunto de instruções de "conhecimento especializado" que o Claude carrega na memória.
+
+Ao instalar o ShieldCode, o Claude passa a seguir um rigoroso guia de boas práticas de segurança sempre que identifica que está lidando com operações sensíveis, como acesso a bancos de dados, autenticação de usuários ou manipulação de arquivos.
+
+## Alinhamento com o OWASP Top 10
+
+O ShieldCode foi projetado com foco direto nas vulnerabilidades listadas no OWASP Top 10. Ele impõe regras automáticas que impedem o Claude de sugerir padrões inseguros.
+
+### Prevenção de Injeção
+
+Sempre que o Claude escreve uma consulta a um banco de dados, o ShieldCode exige o uso de consultas parametrizadas. A concatenação de variáveis diretamente na string da query é bloqueada pelas diretrizes da skill, eliminando o risco de SQL Injection na raiz.
+
+### Autenticação e Criptografia
+
+O ShieldCode proíbe o uso de algoritmos de hash fracos. Ele direciona o Claude a utilizar padrões modernos como bcrypt ou argon2 para senhas, além de garantir que JWTs (JSON Web Tokens) sejam validados corretamente, com verificação de expiração e assinatura em todos os fluxos.
+
+### Segurança de Arquivos e Path Traversal
+
+Manipular arquivos baseando-se em inputs de usuários é um vetor clássico de ataque. A skill força a implementação de verificações de sanidade em caminhos de arquivos, impedindo que um atacante consiga acessar arquivos sensíveis do sistema através de manipulação de strings (../../etc/passwd).
+
+## Tratamento de Erros de Nível de Produção
+
+Segurança não é apenas sobre prevenir ataques externos, mas também sobre como o sistema se comporta quando algo dá errado. O ShieldCode introduz padrões de tratamento de erro que são essenciais para sistemas resilientes:
+
+1. **Hierarquia de Exceções:** Em vez de lançar erros genéricos, a IA é instruída a criar e utilizar classes de erro tipadas.
+2. **Logs Seguros:** Garante que informações sensíveis (PII ou segredos) nunca sejam escritas nos logs.
+3. **Resiliência:** Implementa automaticamente padrões de "retry" com backoff exponencial em chamadas de APIs externas.
+
+## Conclusão
+
+Delegar a escrita de código para uma IA exige uma camada de supervisão que muitas vezes o desenvolvedor não tem tempo de exercer em todos os detalhes. O ShieldCode automatiza essa supervisão, garantindo que o Claude Code produza software que não apenas funciona, mas que é seguro e pronto para produção.
+
+Ao adotar essa skill, você transforma o seu assistente de IA de um simples gerador de código em um parceiro de desenvolvimento que compreende e aplica os princípios fundamentais da segurança da informação.
+
+---
+**Sobre o autor:**
+Nikolas de Hor é desenvolvedor de software em Goiânia, focado em criar ferramentas que tornam o desenvolvimento com IA mais seguro e eficiente.
+Contato: nikolasdehor79@gmail.com
diff --git a/docs/linkedin-post-pt.md b/docs/linkedin-post-pt.md
new file mode 100644
index 0000000..1280503
--- /dev/null
+++ b/docs/linkedin-post-pt.md
@@ -0,0 +1,29 @@
+# LinkedIn Post: Segurança em Primeiro Lugar com ShieldCode
+
+Você confia cegamente no código que a IA gera para você?
+
+A produtividade que ganhamos com ferramentas como o Claude Code é inegável, mas ela traz um desafio silencioso: a segurança da informação. Muitas vezes, a IA prioriza a entrega rápida de uma funcionalidade, deixando de lado validações críticas, padrões de criptografia modernos e o tratamento adequado de exceções.
+
+Para garantir que o uso de IA no desenvolvimento não comprometa a segurança das nossas aplicações, desenvolvi o **ShieldCode**.
+
+O ShieldCode é uma skill especializada para o Claude Code. Ele não é uma biblioteca externa, mas sim uma camada de inteligência que "ensina" o Claude a adotar padrões de segurança de nível de produção em tempo real.
+
+O que o ShieldCode entrega:
+
+🛡️ **Proteção OWASP Top 10:** Garante o uso de consultas parametrizadas contra SQL Injection e validações rigorosas de input contra XSS e Path Traversal.
+🛡️ **Criptografia Moderna:** Orienta o uso de algoritmos robustos (bcrypt, argon2) e gerenciamento seguro de tokens e segredos.
+🛡️ **Resiliência e Erros:** Implementa padrões de tratamento de erro que evitam vazamento de informações sensíveis e garantem a estabilidade do sistema.
+🛡️ **Zero Dependências:** Como é uma skill do Claude Code, ele não adiciona peso ao seu projeto, atuando diretamente na "mente" da IA.
+
+Segurança não deve ser um pensamento tardio ou uma etapa final antes do deploy. Ela deve estar presente em cada linha de código escrita, inclusive aquelas geradas por assistentes.
+
+O ShieldCode é open source e está disponível para quem deseja tornar o desenvolvimento com IA mais maduro e seguro.
+
+Confira o projeto no GitHub e veja como ele pode ajudar o seu time a entregar código blindado.
+
+---
+**Nikolas de Hor**
+Desenvolvedor de Software | Goiânia, Brasil
+Projeto: https://github.com/nikolasdehor/shieldcode
+
+#AI #CyberSecurity #CloudCode #SoftwareEngineering #OpenSource #DevSecOps #ShieldCode
diff --git a/docs/x-thread-pt.md b/docs/x-thread-pt.md
new file mode 100644
index 0000000..2ade2d6
--- /dev/null
+++ b/docs/x-thread-pt.md
@@ -0,0 +1,20 @@
+# X Thread: ShieldCode e Segurança com IA 🛡️💻
+
+1/7 Você sabia que mais de 60% do código gerado por IA contém pelo menos uma vulnerabilidade de segurança? A IA é rápida, mas sem as diretrizes certas, ela costuma pegar atalhos perigosos. 🧵
+
+2/7 Para resolver isso, criei o **ShieldCode**. É uma skill para o Claude Code que força a IA a seguir padrões de segurança rigorosos e tratamento de erros de nível de produção. Não é um pacote npm ou pip, é inteligência acoplada ao seu assistente.
+
+3/7 O foco principal é o OWASP Top 10. Com o ShieldCode ativo, o Claude para de sugerir concatenação de strings em SQL e passa a usar consultas parametrizadas automaticamente. Segurança contra injeção por padrão.
+
+4/7 Criptografia também é prioridade. Esqueça sugestões de MD5 ou SHA1 para senhas. A skill direciona o Claude para o uso de bcrypt, argon2 e gerenciamento seguro de JWTs, garantindo que a autenticação da sua app seja robusta.
+
+5/7 Além da segurança, o ShieldCode melhora a resiliência. Ele força o tratamento de exceções tipadas e implementa padrões de retry com backoff em chamadas de API. O código para de "quebrar silenciosamente" e passa a falhar com elegância.
+
+6/7 O melhor de tudo? É fácil de instalar e funciona de forma transparente dentro do Claude Code. Basta um comando para garantir que o seu assistente de IA esteja blindado contra padrões de código inseguros.
+
+7/7 Quer parar de enviar vulnerabilidades para produção? Confira o ShieldCode no GitHub. Projeto open source feito com cuidado para tornar o desenvolvimento com IA mais profissional.
+
+Link: https://github.com/nikolasdehor/shieldcode
+Por Nikolas de Hor em Goiânia.
+
+#ClaudeCode #CyberSecurity #OWASP #Programming #AI #OpenSource
From 7cd9eabc35606a48d66f3e1d79edc76857743baa Mon Sep 17 00:00:00 2001
From: Nikolas de Hor
Date: Tue, 19 May 2026 12:24:45 -0300
Subject: [PATCH 2/2] feat: adicionar 4 exemplos OWASP (sql-injection, xss,
ssrf, path-traversal)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Cada exemplo inclui:
- vulnerable.py: código com a vulnerabilidade e comentários explicando o risco
- hardened.py: versão segura com comentários explicando a correção
- README.md: contexto do ataque, fix e como ShieldCode previne em Claude Code
Cenários cobertos: SQL Injection, XSS, SSRF e Path Traversal (FastAPI/Python)
---
examples/path-traversal/README.md | 35 +++++++++++++++++++
examples/path-traversal/hardened.py | 34 +++++++++++++++++++
examples/path-traversal/vulnerable.py | 21 ++++++++++++
examples/sql-injection/README.md | 36 ++++++++++++++++++++
examples/sql-injection/hardened.py | 45 ++++++++++++++++++++++++
examples/sql-injection/vulnerable.py | 35 +++++++++++++++++++
examples/ssrf/README.md | 36 ++++++++++++++++++++
examples/ssrf/hardened.py | 49 +++++++++++++++++++++++++++
examples/ssrf/vulnerable.py | 24 +++++++++++++
examples/xss/README.md | 34 +++++++++++++++++++
examples/xss/hardened.py | 34 +++++++++++++++++++
examples/xss/vulnerable.py | 20 +++++++++++
12 files changed, 403 insertions(+)
create mode 100644 examples/path-traversal/README.md
create mode 100644 examples/path-traversal/hardened.py
create mode 100644 examples/path-traversal/vulnerable.py
create mode 100644 examples/sql-injection/README.md
create mode 100644 examples/sql-injection/hardened.py
create mode 100644 examples/sql-injection/vulnerable.py
create mode 100644 examples/ssrf/README.md
create mode 100644 examples/ssrf/hardened.py
create mode 100644 examples/ssrf/vulnerable.py
create mode 100644 examples/xss/README.md
create mode 100644 examples/xss/hardened.py
create mode 100644 examples/xss/vulnerable.py
diff --git a/examples/path-traversal/README.md b/examples/path-traversal/README.md
new file mode 100644
index 0000000..ab0a07d
--- /dev/null
+++ b/examples/path-traversal/README.md
@@ -0,0 +1,35 @@
+# Path Traversal Scenario
+
+This FastAPI example downloads invoice PDFs from a storage directory.
+
+## Attack
+
+The vulnerable endpoint accepts:
+
+```text
+GET /invoices/download?filename=../../.env
+```
+
+`vulnerable.py` joins the raw filename with the invoice directory. `../`
+segments can escape the intended folder and read application secrets or system
+files if the process has permission.
+
+## Fix
+
+`hardened.py` applies ShieldCode's file security rules:
+
+- Validate filenames with an allowlist regex.
+- Resolve the final path before reading it.
+- Verify the resolved path stays inside the allowed base directory.
+- Require a regular file and return the expected PDF media type.
+
+## ShieldCode in action
+
+Prompt Claude Code after installing ShieldCode:
+
+```text
+Create a FastAPI endpoint to download invoice files by filename.
+```
+
+With ShieldCode active, Claude should reject arbitrary path input and keep all
+file access inside an explicitly approved directory.
diff --git a/examples/path-traversal/hardened.py b/examples/path-traversal/hardened.py
new file mode 100644
index 0000000..6a5704a
--- /dev/null
+++ b/examples/path-traversal/hardened.py
@@ -0,0 +1,34 @@
+from pathlib import Path
+import re
+
+from fastapi import FastAPI, HTTPException, Query
+from fastapi.responses import FileResponse
+
+app = FastAPI(title="Hardened invoice download")
+
+BASE_DIR = Path("storage/invoices").resolve()
+SAFE_INVOICE_NAME = re.compile(r"^[a-zA-Z0-9_-]+\.pdf$")
+
+
+@app.get("/invoices/download")
+def download_invoice(filename: str = Query(..., min_length=5, max_length=80)):
+ # FIX: allowlist the exact filename format the endpoint supports. Path
+ # separators, hidden files, and alternate extensions are rejected up front.
+ if not SAFE_INVOICE_NAME.fullmatch(filename):
+ raise HTTPException(status_code=400, detail="Invalid invoice filename")
+
+ target = (BASE_DIR / filename).resolve()
+
+ # FIX: resolve the final path and verify it remains inside BASE_DIR before
+ # reading. This defends even if symlinks or unusual path segments appear.
+ if BASE_DIR not in target.parents:
+ raise HTTPException(status_code=400, detail="Invalid invoice path")
+
+ if not target.is_file():
+ raise HTTPException(status_code=404, detail="Invoice not found")
+
+ return FileResponse(
+ target,
+ media_type="application/pdf",
+ filename=filename,
+ )
diff --git a/examples/path-traversal/vulnerable.py b/examples/path-traversal/vulnerable.py
new file mode 100644
index 0000000..45e983e
--- /dev/null
+++ b/examples/path-traversal/vulnerable.py
@@ -0,0 +1,21 @@
+from pathlib import Path
+
+from fastapi import FastAPI, HTTPException, Query
+from fastapi.responses import FileResponse
+
+app = FastAPI(title="Vulnerable invoice download")
+
+BASE_DIR = Path("storage/invoices")
+
+
+@app.get("/invoices/download")
+def download_invoice(filename: str = Query(...)):
+ # VULNERABLE: joining untrusted input allows ../ path traversal. A request
+ # like filename=../../.env can escape storage/invoices and read server files.
+ target = BASE_DIR / filename
+
+ if not target.exists():
+ raise HTTPException(status_code=404, detail="Invoice not found")
+
+ # VULNERABLE: the response returns whatever path the attacker reached.
+ return FileResponse(target)
diff --git a/examples/sql-injection/README.md b/examples/sql-injection/README.md
new file mode 100644
index 0000000..9d7a693
--- /dev/null
+++ b/examples/sql-injection/README.md
@@ -0,0 +1,36 @@
+# SQL Injection Scenario
+
+This FastAPI example shows a customer search endpoint that looks normal but
+builds SQL by concatenating a query string.
+
+## Attack
+
+The vulnerable endpoint accepts:
+
+```text
+GET /customers?q=' OR '1'='1
+```
+
+Because `vulnerable.py` inserts `q` directly into the SQL string, the attacker
+can change the meaning of the query and return records they should not see.
+Verbose database errors also leak schema hints.
+
+## Fix
+
+`hardened.py` applies ShieldCode's SQL injection rules:
+
+- Validate input length with FastAPI `Query`.
+- Use parameterized placeholders instead of string formatting.
+- Escape `%`, `_`, and `\` before using input in `LIKE`.
+- Log internal failures without returning raw database errors.
+
+## ShieldCode in action
+
+Prompt Claude Code after installing ShieldCode:
+
+```text
+Write a FastAPI endpoint to search customers by name or email.
+```
+
+With ShieldCode active, Claude should avoid f-strings in SQL and produce the
+parameterized pattern shown in `hardened.py`.
diff --git a/examples/sql-injection/hardened.py b/examples/sql-injection/hardened.py
new file mode 100644
index 0000000..5f7d42d
--- /dev/null
+++ b/examples/sql-injection/hardened.py
@@ -0,0 +1,45 @@
+from fastapi import FastAPI, HTTPException, Query
+import logging
+import sqlite3
+
+app = FastAPI(title="Hardened SQL search")
+logger = logging.getLogger("shieldcode.sql")
+
+
+def get_connection() -> sqlite3.Connection:
+ connection = sqlite3.connect("shop.db")
+ connection.row_factory = sqlite3.Row
+ return connection
+
+
+def escape_like(value: str) -> str:
+ # FIX: escape LIKE wildcards so user input stays data, not pattern control.
+ return value.replace("\\", "\\\\").replace("%", "\\%").replace("_", "\\_")
+
+
+@app.get("/customers")
+def search_customers(q: str = Query(..., min_length=1, max_length=80)):
+ connection = get_connection()
+ pattern = f"%{escape_like(q)}%"
+
+ # FIX: parameterized placeholders keep the SQL structure fixed. The
+ # database driver sends attacker input as values, not executable SQL.
+ sql = """
+ SELECT id, email, full_name
+ FROM customers
+ WHERE full_name LIKE ? ESCAPE '\\'
+ OR email LIKE ? ESCAPE '\\'
+ ORDER BY full_name
+ """
+
+ try:
+ rows = connection.execute(sql, (pattern, pattern)).fetchall()
+ except sqlite3.DatabaseError as exc:
+ # FIX: log the internal error server-side, return a generic client
+ # message, and avoid leaking table or column names.
+ logger.exception("customer_search_failed")
+ raise HTTPException(status_code=500, detail="Search failed") from exc
+ finally:
+ connection.close()
+
+ return {"results": [dict(row) for row in rows]}
diff --git a/examples/sql-injection/vulnerable.py b/examples/sql-injection/vulnerable.py
new file mode 100644
index 0000000..95970f8
--- /dev/null
+++ b/examples/sql-injection/vulnerable.py
@@ -0,0 +1,35 @@
+from fastapi import FastAPI, HTTPException, Query
+import sqlite3
+
+app = FastAPI(title="Vulnerable SQL search")
+
+
+def get_connection() -> sqlite3.Connection:
+ connection = sqlite3.connect("shop.db")
+ connection.row_factory = sqlite3.Row
+ return connection
+
+
+@app.get("/customers")
+def search_customers(q: str = Query(..., min_length=1)):
+ connection = get_connection()
+
+ # VULNERABLE: user-controlled input is interpolated directly into SQL.
+ # An attacker can send q=' OR '1'='1 to turn the WHERE clause into a
+ # tautology, or append UNION queries to read unrelated tables.
+ sql = (
+ "SELECT id, email, full_name FROM customers "
+ f"WHERE full_name LIKE '%{q}%' OR email LIKE '%{q}%' "
+ "ORDER BY full_name"
+ )
+
+ try:
+ rows = connection.execute(sql).fetchall()
+ except sqlite3.DatabaseError as exc:
+ # VULNERABLE: returning raw database errors can reveal schema details
+ # that make injection attacks easier to refine.
+ raise HTTPException(status_code=500, detail=str(exc)) from exc
+ finally:
+ connection.close()
+
+ return {"results": [dict(row) for row in rows]}
diff --git a/examples/ssrf/README.md b/examples/ssrf/README.md
new file mode 100644
index 0000000..17f1b20
--- /dev/null
+++ b/examples/ssrf/README.md
@@ -0,0 +1,36 @@
+# SSRF Scenario
+
+This FastAPI example tests whether a webhook URL is reachable.
+
+## Attack
+
+The vulnerable endpoint accepts:
+
+```text
+GET /webhook/test?url=http://169.254.169.254/latest/meta-data/
+```
+
+Because `vulnerable.py` fetches any URL supplied by the caller, an attacker can
+make the server connect to cloud metadata services, localhost admin ports, or
+private network hosts that are not reachable from the public internet.
+
+## Fix
+
+`hardened.py` applies ShieldCode's SSRF controls:
+
+- Require `https`.
+- Allow only known webhook hosts.
+- Reject obvious literal private, loopback, and link-local IPs.
+- Disable redirects to avoid allowlist bypasses.
+- Return minimal reachability metadata instead of proxying response bodies.
+
+## ShieldCode in action
+
+Prompt Claude Code after installing ShieldCode:
+
+```text
+Build a FastAPI endpoint that tests whether a webhook URL is reachable.
+```
+
+With ShieldCode active, Claude should ask for or define an allowlist and avoid
+fetching arbitrary user-provided URLs.
diff --git a/examples/ssrf/hardened.py b/examples/ssrf/hardened.py
new file mode 100644
index 0000000..a3c7dc5
--- /dev/null
+++ b/examples/ssrf/hardened.py
@@ -0,0 +1,49 @@
+from ipaddress import ip_address
+from urllib.parse import urlparse
+
+from fastapi import FastAPI, HTTPException, Query
+import httpx
+
+app = FastAPI(title="Hardened webhook tester")
+
+ALLOWED_WEBHOOK_HOSTS = {"hooks.stripe.com", "api.github.com", "discord.com"}
+
+
+def validate_webhook_url(url: str) -> str:
+ parsed = urlparse(url)
+
+ # FIX: allow only HTTPS and an explicit host allowlist. This prevents users
+ # from turning the API into a proxy for arbitrary internal services.
+ if parsed.scheme != "https":
+ raise HTTPException(status_code=400, detail="Webhook URL must use HTTPS")
+ if parsed.hostname not in ALLOWED_WEBHOOK_HOSTS:
+ raise HTTPException(status_code=400, detail="Webhook host is not allowed")
+
+ # FIX: reject literal private, loopback, and link-local IP addresses. Host
+ # allowlists are still the main control; this blocks obvious bypass attempts.
+ try:
+ host_ip = ip_address(parsed.hostname)
+ except ValueError:
+ return url
+
+ if host_ip.is_private or host_ip.is_loopback or host_ip.is_link_local:
+ raise HTTPException(status_code=400, detail="Webhook host is not allowed")
+ return url
+
+
+@app.get("/webhook/test")
+async def test_webhook(url: str = Query(..., max_length=300)):
+ safe_url = validate_webhook_url(url)
+
+ try:
+ async with httpx.AsyncClient(
+ timeout=httpx.Timeout(3.0),
+ follow_redirects=False,
+ ) as client:
+ response = await client.get(safe_url)
+ except httpx.HTTPError as exc:
+ raise HTTPException(status_code=502, detail="Webhook test failed") from exc
+
+ # FIX: return only minimal metadata. Do not proxy response bodies or headers
+ # from third-party systems back to the caller.
+ return {"status_code": response.status_code, "reachable": response.is_success}
diff --git a/examples/ssrf/vulnerable.py b/examples/ssrf/vulnerable.py
new file mode 100644
index 0000000..71094e0
--- /dev/null
+++ b/examples/ssrf/vulnerable.py
@@ -0,0 +1,24 @@
+from fastapi import FastAPI, HTTPException, Query
+import httpx
+
+app = FastAPI(title="Vulnerable webhook tester")
+
+
+@app.get("/webhook/test")
+async def test_webhook(url: str = Query(...)):
+ # VULNERABLE: the server fetches an arbitrary user-supplied URL. Attackers
+ # can target internal services such as http://169.254.169.254/latest/meta-data
+ # or http://localhost:8000/admin from the server's network position.
+ try:
+ async with httpx.AsyncClient(timeout=5.0) as client:
+ response = await client.get(url)
+ except httpx.HTTPError as exc:
+ raise HTTPException(status_code=502, detail=str(exc)) from exc
+
+ # VULNERABLE: reflecting status and body from internal services can expose
+ # secrets, metadata, or private admin responses.
+ return {
+ "status_code": response.status_code,
+ "headers": dict(response.headers),
+ "body": response.text[:2000],
+ }
diff --git a/examples/xss/README.md b/examples/xss/README.md
new file mode 100644
index 0000000..0f49611
--- /dev/null
+++ b/examples/xss/README.md
@@ -0,0 +1,34 @@
+# Cross-Site Scripting Scenario
+
+This FastAPI example renders a comment preview from submitted form fields.
+
+## Attack
+
+An attacker posts a comment such as:
+
+```html
+
+```
+
+`vulnerable.py` inserts that string directly into an HTML response. The browser
+parses it as markup and runs the event handler for any user who previews it.
+
+## Fix
+
+`hardened.py` applies ShieldCode's XSS rules:
+
+- Escape user-controlled output with `html.escape`.
+- Keep untrusted content as text, not markup.
+- Add a restrictive Content Security Policy.
+- Send defensive browser headers.
+
+## ShieldCode in action
+
+Prompt Claude Code after installing ShieldCode:
+
+```text
+Create a FastAPI route that previews a submitted comment as HTML.
+```
+
+With ShieldCode active, Claude should treat every form value as untrusted and
+escape it before returning `HTMLResponse`.
diff --git a/examples/xss/hardened.py b/examples/xss/hardened.py
new file mode 100644
index 0000000..38f2cb1
--- /dev/null
+++ b/examples/xss/hardened.py
@@ -0,0 +1,34 @@
+from html import escape
+
+from fastapi import FastAPI, Form
+from fastapi.responses import HTMLResponse
+
+app = FastAPI(title="Hardened comment preview")
+
+
+@app.post("/preview", response_class=HTMLResponse)
+def preview_comment(display_name: str = Form(...), comment: str = Form(...)):
+ # FIX: escape all user-controlled output before embedding it in HTML.
+ # The browser receives text such as <script> instead of executable
+ # markup, so attacker payloads render harmlessly.
+ safe_name = escape(display_name, quote=True)
+ safe_comment = escape(comment, quote=True)
+
+ html = f"""
+
+
+
+
+
+ Preview for {safe_name}
+
+
+
+ """
+ response = HTMLResponse(content=html)
+
+ # FIX: security headers reduce impact if a future rendering bug appears.
+ response.headers["X-Content-Type-Options"] = "nosniff"
+ response.headers["X-Frame-Options"] = "DENY"
+ response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin"
+ return response
diff --git a/examples/xss/vulnerable.py b/examples/xss/vulnerable.py
new file mode 100644
index 0000000..3d9f864
--- /dev/null
+++ b/examples/xss/vulnerable.py
@@ -0,0 +1,20 @@
+from fastapi import FastAPI, Form
+from fastapi.responses import HTMLResponse
+
+app = FastAPI(title="Vulnerable comment preview")
+
+
+@app.post("/preview", response_class=HTMLResponse)
+def preview_comment(display_name: str = Form(...), comment: str = Form(...)):
+ # VULNERABLE: user-controlled strings are inserted directly into an HTML
+ # response. If comment contains