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}

+
{safe_comment}
+ + + """ + 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