From fa451bee879c0dfee1f60d2b7faa575760d81421 Mon Sep 17 00:00:00 2001 From: Nikolas de Hor Date: Wed, 20 May 2026 02:27:14 -0300 Subject: [PATCH] docs: site mkdocs-material + auto-deploy GitHub Pages Site de documentacao com tema vermelho/laranja (cores de seguranca). Conteudo: - index.md: visao geral + porque ShieldCode + como funciona - getting-started/: install, quickstart, como a skill funciona - vulns/: sql-injection, xss, ssrf, path-traversal - frameworks/: django, fastapi, express, spring boot - comparison.md: vs SAST tools, OWASP cheat sheets, fine-tuning - contributing, changelog Workflow deploya em https://nikolasdehor.github.io/shieldcode/ a cada push em main que toque docs/, mkdocs.yml, CHANGELOG, skills/, examples/. --- .github/workflows/docs.yml | 61 ++ .gitignore | 1 + docs/changelog.md | 1 + docs/comparison.md | 66 ++ docs/contributing.md | 47 + docs/frameworks/django.md | 73 ++ docs/frameworks/express.md | 133 +++ docs/frameworks/fastapi.md | 104 +++ docs/frameworks/spring.md | 148 ++++ docs/getting-started/how.md | 78 ++ docs/getting-started/install.md | 47 + docs/getting-started/quickstart.md | 63 ++ docs/index.html | 1270 ---------------------------- docs/index.md | 77 ++ docs/vulns/path-traversal.md | 98 +++ docs/vulns/sql-injection.md | 98 +++ docs/vulns/ssrf.md | 82 ++ docs/vulns/xss.md | 96 +++ mkdocs.yml | 109 +++ 19 files changed, 1382 insertions(+), 1270 deletions(-) create mode 100644 .github/workflows/docs.yml create mode 100644 docs/changelog.md create mode 100644 docs/comparison.md create mode 100644 docs/contributing.md create mode 100644 docs/frameworks/django.md create mode 100644 docs/frameworks/express.md create mode 100644 docs/frameworks/fastapi.md create mode 100644 docs/frameworks/spring.md create mode 100644 docs/getting-started/how.md create mode 100644 docs/getting-started/install.md create mode 100644 docs/getting-started/quickstart.md delete mode 100644 docs/index.html create mode 100644 docs/index.md create mode 100644 docs/vulns/path-traversal.md create mode 100644 docs/vulns/sql-injection.md create mode 100644 docs/vulns/ssrf.md create mode 100644 docs/vulns/xss.md create mode 100644 mkdocs.yml diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..7a75794 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,61 @@ +name: Docs + +on: + push: + branches: [main] + paths: + - "docs/**" + - "mkdocs.yml" + - ".github/workflows/docs.yml" + - "CHANGELOG.md" + - "skills/**" + - "examples/**" + - "README.md" + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: false + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - name: Cache pip + uses: actions/cache@v4 + with: + path: ~/.cache/pip + key: docs-pip + - name: Install dependencies + run: | + pip install \ + mkdocs-material \ + mkdocs-mermaid2-plugin \ + mkdocs-git-revision-date-localized-plugin \ + pymdown-extensions + - name: Build site + run: mkdocs build --strict + - uses: actions/upload-pages-artifact@v3 + with: + path: site/ + + deploy: + needs: build + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index 7cfd02b..6c0575f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .DS_Store *.log node_modules/ +site/ diff --git a/docs/changelog.md b/docs/changelog.md new file mode 100644 index 0000000..786b75d --- /dev/null +++ b/docs/changelog.md @@ -0,0 +1 @@ +--8<-- "CHANGELOG.md" diff --git a/docs/comparison.md b/docs/comparison.md new file mode 100644 index 0000000..ac034a5 --- /dev/null +++ b/docs/comparison.md @@ -0,0 +1,66 @@ +# Comparativo + +ShieldCode vs outras formas de hardening de código. + +## Snyk Code, GitHub Advanced Security, Sonar + +| Aspecto | ShieldCode | SAST tools | +|---------|-----------|------------| +| Quando age | Durante geração | Após código escrito | +| Granularidade | Padrão por padrão | Vulnerabilidade por vulnerabilidade | +| Custo | $0 | Pago (SaaS) | +| Cobertura | Padrões comuns (4 hoje, mais vindo) | Bibliotecas grandes de CVEs | +| Falsos positivos | Zero (não detecta, previne) | Médio-alto | +| Educação | Alta (explica cada padrão) | Baixa (só aponta) | + +**Combine ambos**: ShieldCode previne os 80% mais comuns durante a geração, SAST cobre os 20% restantes e CVEs específicos de libs. + +## Bandit / Semgrep + +Linters de segurança open source. ShieldCode complementa: + +- **Bandit/Semgrep**: detectam após escrita +- **ShieldCode**: previne durante geração + +Bandit pode rodar em CI verificando que ShieldCode foi aplicado. Defense-in-depth. + +## OWASP Cheat Sheets + +OWASP mantém cheat sheets pra cada vulnerabilidade. ShieldCode é a **versão acionável** dessas cheat sheets pra agentes de IA: + +- OWASP cheat sheet diz "use parametrização em SQL" +- ShieldCode FAZ o Claude usar parametrização toda vez + +## Por que skill é melhor que prompt engineering manual + +Sem ShieldCode, você teria que lembrar de pedir: + +> "Faz endpoint Y mas usa parametrização SQL pra prevenir injection, escape HTML do input do usuário, valida URL antes de fazer request, canonicaliza path antes de salvar..." + +Com ShieldCode, você só pede: + +> "Faz endpoint Y" + +E a skill garante todos os padrões. Você não precisa lembrar — o sistema lembra por você. + +## Por que skill em vez de fine-tuning + +Fine-tuning seria a melhor solução em teoria. Mas: + +- Caro (precisa de dataset grande, GPU, tempo) +- Específico de modelo (não funciona em todos os Claude / GPT / Gemini) +- Não é atualizável rápido (padrões de segurança evoluem) + +Skill em markdown é hackável, gratuita, atualizável em segundos. + +## Limitações + +ShieldCode reduz risco de classes comuns de vulnerabilidade. NÃO substitui: + +- **Pentesting** (encontra problemas que escapam de SAST e generators) +- **Code review** humano (julgamento contextual) +- **Threat modeling** (decisões arquiteturais) +- **Dependency scanning** (CVEs em libs) +- **Runtime protection** (WAF, RASP) + +É uma layer adicional, não a única. diff --git a/docs/contributing.md b/docs/contributing.md new file mode 100644 index 0000000..710cd7f --- /dev/null +++ b/docs/contributing.md @@ -0,0 +1,47 @@ +# Contribuir + +## Como ajudar + +- **Novos cenários OWASP**: adicionar exemplos em `examples/` +- **Mais frameworks**: criar páginas em `docs/frameworks/` +- **Issues**: reportar bugs ou pedir features em [github.com/nikolasdehor/shieldcode/issues](https://github.com/nikolasdehor/shieldcode/issues) +- **Casos reais**: compartilhar como você usa + +## Adicionar novo cenário OWASP + +1. Crie `examples//` com: + - `vulnerable.py` — código exemplo VULNERÁVEL com comentários explicando o ataque + - `hardened.py` — versão SEGURA com comentários explicando a mitigação + - `README.md` — explicação curta + referências OWASP / CWE +2. Atualize `skills/shieldcode/SKILL.md` adicionando a regra +3. Crie página em `docs/vulns/.md` +4. Adicione à navegação em `mkdocs.yml` + +## Setup dev + +```bash +git clone https://github.com/nikolasdehor/shieldcode +cd shieldcode +./install.sh --scope=project +``` + +## Testar a skill localmente + +No Claude Code: + +``` +"Use a skill shieldcode. Faz endpoint Flask que recebe filename do usuário e serve o arquivo." +``` + +Você deve receber: + +- Código `hardened` (com canonicalização de path) +- Explicação clara sobre path traversal +- Sugestão de defenses adicionais + +## Padrões + +- Português pt-BR com acentos corretos em docs e comentários +- Inglês em código (identifiers, variable names) +- Mensagens de commit em imperativo, sem AI footers +- Markdown limpo, code blocks com linguagem especificada diff --git a/docs/frameworks/django.md b/docs/frameworks/django.md new file mode 100644 index 0000000..66d4564 --- /dev/null +++ b/docs/frameworks/django.md @@ -0,0 +1,73 @@ +# Django + +ShieldCode aplica padrões idiomáticos do Django para cada cenário. + +## SQL Injection + +O Django ORM é **safe by default**. ShieldCode reforça: + +- ✅ Use querysets: `User.objects.filter(name=name)` +- ❌ Evite `.raw()` ou `.extra()` com input do usuário +- Se precisar de SQL puro, use parâmetros: + ```python + User.objects.raw("SELECT * FROM users WHERE name = %s", [name]) + ``` + +## XSS + +Django templates **auto-escape** por default: + +```html +{# SEGURO automaticamente #} +

Olá {{ nome }}

+``` + +ShieldCode alerta quando vir `|safe`, `mark_safe()` ou `{% autoescape off %}`. + +## CSRF + +Django tem CSRF protection embutido. ShieldCode garante: + +- `{% csrf_token %}` em todo formulário +- `@csrf_exempt` só quando absolutamente necessário (e justificado em comentário) + +## SSRF + +Django não tem proteção embutida. ShieldCode aplica os padrões genéricos de [SSRF](../vulns/ssrf.md). + +## Settings de segurança + +ShieldCode sugere garantir no `settings.py`: + +```python +SECURE_SSL_REDIRECT = True +SESSION_COOKIE_SECURE = True +CSRF_COOKIE_SECURE = True +SECURE_HSTS_SECONDS = 31536000 +SECURE_HSTS_INCLUDE_SUBDOMAINS = True +SECURE_HSTS_PRELOAD = True +SECURE_CONTENT_TYPE_NOSNIFF = True +SECURE_BROWSER_XSS_FILTER = True +X_FRAME_OPTIONS = "DENY" +``` + +## Dependências auxiliares + +ShieldCode sugere: + +- `django-axes` (rate limit de login) +- `django-csp` (Content-Security-Policy) +- `django-security` (vários middlewares de segurança) +- `bandit` em CI + +## Exemplo prático + +> "Faz um endpoint Django REST que cadastra usuário com email e senha" + +ShieldCode entrega: + +- Serializer com validação (`EmailField`, `min_length=8` na senha) +- View com `permission_classes = [AllowAny]` explícito (para cadastro) +- Senha hasheada via `set_password()` (não em plaintext) +- Rate limit via `django-axes` ou DRF throttling +- Resposta sem expor IDs internos diff --git a/docs/frameworks/express.md b/docs/frameworks/express.md new file mode 100644 index 0000000..93c9943 --- /dev/null +++ b/docs/frameworks/express.md @@ -0,0 +1,133 @@ +# Express / Node.js + +ShieldCode aplica padrões idiomáticos do Express e ecossistema Node. + +## SQL Injection + +Use ORM ou drivers com parametrização: + +```javascript +// Prisma (recomendado) +const user = await prisma.user.findUnique({ where: { email } }); + +// pg (driver Postgres) parametrizado +await db.query("SELECT * FROM users WHERE name = $1", [name]); + +// mysql2 prepared statements +await db.execute("SELECT * FROM users WHERE name = ?", [name]); +``` + +## Input validation + +Use Zod: + +```typescript +import { z } from "zod"; + +const UserSchema = z.object({ + email: z.string().email(), + password: z.string().min(8).max(128), +}); + +app.post("/users", (req, res) => { + const parsed = UserSchema.safeParse(req.body); + if (!parsed.success) { + return res.status(400).json({ errors: parsed.error.errors }); + } + // parsed.data tem tipos corretos +}); +``` + +## XSS prevention + +Use template engines com auto-escape (Pug, EJS com `<%- %>` cuidadoso, Handlebars): + +```javascript +// Sem escape: VULNERÁVEL em EJS +<%- userInput %> + +// Com escape: SEGURO em EJS +<%= userInput %> +``` + +Em SPAs (React, Vue, Svelte), frameworks escapam por padrão. + +## Helmet (security headers) + +```javascript +import helmet from "helmet"; + +app.use(helmet()); // CSP, HSTS, X-Frame-Options, etc + +app.use(helmet.contentSecurityPolicy({ + directives: { + defaultSrc: ["'self'"], + scriptSrc: ["'self'", "'nonce-RANDOM'"], + }, +})); +``` + +## Auth (passport / jose) + +Para JWT, prefira `jose` (moderno) ou `jsonwebtoken`: + +```javascript +import { SignJWT, jwtVerify } from "jose"; + +const secret = new TextEncoder().encode(process.env.JWT_SECRET); + +const token = await new SignJWT({ sub: userId }) + .setProtectedHeader({ alg: "HS256" }) + .setExpirationTime("15m") + .sign(secret); +``` + +Senha: use `bcrypt` ou `argon2`: + +```javascript +import bcrypt from "bcrypt"; + +const hash = await bcrypt.hash(password, 12); +const valid = await bcrypt.compare(password, hash); +``` + +## Rate limit + +```javascript +import rateLimit from "express-rate-limit"; + +const loginLimiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 min + max: 5, +}); + +app.post("/login", loginLimiter, loginHandler); +``` + +## CORS + +```javascript +import cors from "cors"; + +app.use(cors({ + origin: ["https://meu-frontend.com"], + credentials: true, +})); +``` + +NUNCA `origin: "*"` com `credentials: true`. + +## Exemplo prático + +> "Faz API Express com login JWT e CRUD de posts" + +ShieldCode entrega: + +- Helmet ON +- Zod validando body em todo POST/PUT +- Senha hasheada com bcrypt cost 12 +- JWT com expiração 15min + refresh tokens +- Rate limit no login (5/15min) +- Posts: SQL via Prisma (zero injection) +- Auth middleware (`Bearer token` obrigatório) +- CORS restrito ao frontend conhecido diff --git a/docs/frameworks/fastapi.md b/docs/frameworks/fastapi.md new file mode 100644 index 0000000..113b66e --- /dev/null +++ b/docs/frameworks/fastapi.md @@ -0,0 +1,104 @@ +# FastAPI + +ShieldCode aplica padrões idiomáticos do FastAPI. + +## SQL Injection + +Use SQLAlchemy / SQLModel: + +```python +# SEGURO +@app.get("/users/{name}") +async def get_user(name: str, db: Session = Depends(get_db)): + user = db.query(User).filter(User.name == name).first() + return user +``` + +Para queries cruas: + +```python +from sqlalchemy import text + +result = db.execute( + text("SELECT * FROM users WHERE name = :name"), + {"name": name}, +) +``` + +## Input validation com Pydantic + +FastAPI usa Pydantic. ShieldCode reforça schemas estritos: + +```python +from pydantic import BaseModel, EmailStr, constr + +class UserCreate(BaseModel): + email: EmailStr + password: constr(min_length=8, max_length=128) + age: int + + @validator("age") + def age_must_be_adult(cls, v): + if v < 18: + raise ValueError("Idade mínima 18") + return v +``` + +Pydantic rejeita input inválido antes da view ver, sem você escrever validação manual. + +## CORS + +FastAPI tem middleware. ShieldCode sugere: + +```python +from fastapi.middleware.cors import CORSMiddleware + +app.add_middleware( + CORSMiddleware, + allow_origins=["https://meu-frontend.com"], # NUNCA "*" + allow_credentials=True, + allow_methods=["GET", "POST"], + allow_headers=["*"], +) +``` + +## Auth (OAuth2 + JWT) + +Pattern padrão FastAPI. ShieldCode garante: + +- Algoritmo `HS256` ou `RS256` (NUNCA `none`) +- Secret forte (`secrets.token_urlsafe(32)`) +- Expiração curta (15-30 min) +- Refresh tokens separados +- Senha hasheada com `bcrypt` ou `argon2` + +## Rate limit + +FastAPI não tem nativo. ShieldCode sugere `slowapi`: + +```python +from slowapi import Limiter +from slowapi.util import get_remote_address + +limiter = Limiter(key_func=get_remote_address) +app.state.limiter = limiter + +@app.post("/login") +@limiter.limit("5/minute") +async def login(...): + ... +``` + +## Exemplo prático + +> "Faz endpoint FastAPI de upload de arquivo" + +ShieldCode entrega: + +- Validação de Content-Type (`UploadFile` do FastAPI já valida) +- Tamanho máximo (`MAX_FILE_SIZE = 10 * 1024 * 1024`) +- Whitelist de extensions +- Salvamento com nome aleatório +- Path canonicalizado com check +- Rate limit +- Auth obrigatório (`Depends(get_current_user)`) diff --git a/docs/frameworks/spring.md b/docs/frameworks/spring.md new file mode 100644 index 0000000..53163d0 --- /dev/null +++ b/docs/frameworks/spring.md @@ -0,0 +1,148 @@ +# Spring Boot + +ShieldCode aplica padrões idiomáticos do Spring Boot. + +## SQL Injection + +Use **Spring Data JPA**: + +```java +// SEGURO - JPA derived query +List findByEmail(String email); + +// SEGURO - JPQL com parâmetros nomeados +@Query("SELECT u FROM User u WHERE u.email = :email") +List findUsers(@Param("email") String email); + +// SEGURO - Native query com parâmetros +@Query(value = "SELECT * FROM users WHERE email = ?1", nativeQuery = true) +List findUsersNative(String email); +``` + +NUNCA concatene strings em `@Query` com input do usuário. + +## Input validation + +Use **Bean Validation** (Jakarta): + +```java +public record UserCreateRequest( + @NotBlank + @Email + String email, + + @Size(min = 8, max = 128) + String password, + + @Min(18) + int age +) {} + +@PostMapping("/users") +public User create(@Valid @RequestBody UserCreateRequest req) { + // req validado automaticamente +} +``` + +## Spring Security + +ShieldCode sugere configuração base segura: + +```java +@Configuration +@EnableWebSecurity +public class SecurityConfig { + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + return http + .csrf(csrf -> csrf.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())) + .authorizeHttpRequests(auth -> auth + .requestMatchers("/api/public/**").permitAll() + .anyRequest().authenticated() + ) + .headers(h -> h + .frameOptions(f -> f.deny()) + .contentSecurityPolicy(c -> c.policyDirectives("default-src 'self'")) + .strictTransportSecurity(s -> s.maxAgeInSeconds(31536000).includeSubDomains(true)) + ) + .build(); + } +} +``` + +## Senha + +```java +@Bean +public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(12); +} + +// Uso +String hashed = passwordEncoder.encode(rawPassword); +boolean valid = passwordEncoder.matches(rawPassword, hashed); +``` + +NUNCA `MD5`, `SHA1` ou senha em plaintext. + +## JWT + +Use **jjwt** ou **Nimbus JOSE+JWT**: + +```java +String token = Jwts.builder() + .setSubject(userId) + .setIssuedAt(new Date()) + .setExpiration(new Date(System.currentTimeMillis() + 15 * 60 * 1000)) + .signWith(SignatureAlgorithm.HS256, secretKey) + .compact(); +``` + +Algoritmo SEMPRE definido (NUNCA aceitar `alg: none`). + +## CORS + +```java +@Configuration +public class CorsConfig { + @Bean + public CorsConfigurationSource corsConfigurationSource() { + CorsConfiguration config = new CorsConfiguration(); + config.setAllowedOrigins(List.of("https://meu-frontend.com")); + config.setAllowCredentials(true); + config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE")); + config.setMaxAge(3600L); + + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", config); + return source; + } +} +``` + +## Rate limit + +Bucket4j é o padrão: + +```java +@Bean +public Bucket loginBucket() { + return Bucket.builder() + .addLimit(Bandwidth.classic(5, Refill.intervally(5, Duration.ofMinutes(15)))) + .build(); +} +``` + +## Exemplo prático + +> "Faz endpoint Spring Boot pra cadastrar usuário" + +ShieldCode entrega: + +- `@Valid` no DTO com `@Email`, `@Size` +- Senha hasheada com BCryptPasswordEncoder cost 12 +- Sem campo "role" no DTO (usuário não escolhe a role) +- Spring Security configurado +- CORS restrito +- Rate limit no endpoint +- Resposta sem expor senha hash diff --git a/docs/getting-started/how.md b/docs/getting-started/how.md new file mode 100644 index 0000000..6d2e7f7 --- /dev/null +++ b/docs/getting-started/how.md @@ -0,0 +1,78 @@ +# Como a skill funciona + +## Arquitetura + +ShieldCode **NÃO** é: + +- ❌ Um package npm / pip / gem +- ❌ Um MCP server +- ❌ Uma extensão de IDE + +ShieldCode **É**: + +- ✅ Um arquivo markdown (`SKILL.md`) que o Claude Code lê +- ✅ Diretrizes que o Claude segue ao gerar código +- ✅ Exemplos de pares "vulnerável vs seguro" pra cada cenário + +## Por que skill em vez de package? + +Padrões de segurança mudam conforme o framework, a linguagem, o contexto. Um package teria que cobrir 100 casos. Uma skill instrui o Claude a **raciocinar** sobre segurança, aplicando o padrão certo para o contexto. + +## Fluxo + +```mermaid +sequenceDiagram + participant U as Você + participant CC as Claude Code + participant S as Skill shieldcode + participant E as Exemplos OWASP + + U->>CC: pede código que toca DB / input / rede + CC->>S: lê SKILL.md + S-->>CC: diretrizes + when to apply + CC->>E: consulta examples/ para padrão específico + E-->>CC: vulnerable.py + hardened.py + CC->>U: gera código baseado em hardened, explica +``` + +## Estrutura no repo + +``` +shieldcode/ +├── skills/shieldcode/SKILL.md # a skill em si +├── examples/ +│ ├── sql-injection/ +│ │ ├── vulnerable.py # NÃO fazer +│ │ └── hardened.py # jeito seguro +│ ├── xss/ +│ ├── ssrf/ +│ └── path-traversal/ +├── install.sh +├── uninstall.sh +└── docs/ # este site +``` + +## SKILL.md em alto nível + +Contém regras como: + +- "Ao gerar código que executa SQL, SEMPRE use parametrização. NUNCA concatene strings." +- "Ao receber input do usuário pra renderizar HTML, SEMPRE escape ou use auto-escape do template engine." +- "Ao construir caminhos de arquivo, SEMPRE canonicalize com `Path.resolve()` e valide que está dentro do diretório esperado." +- "Para cada output, INCLUA breve explicação do porquê o padrão é necessário." + +Cobertura completa em [skills/shieldcode/SKILL.md](https://github.com/nikolasdehor/shieldcode/blob/main/skills/shieldcode/SKILL.md). + +## Por que isso funciona + +LLMs treinam em código GitHub. GitHub tem MUITO código inseguro (tutoriais antigos, código de aprendizado). Sem skill, o Claude tende a reproduzir padrões inseguros. + +Skill estabelece um **prior** que sobrepõe a tendência ruim, forçando o Claude a parar e pensar antes de gerar. + +## Limitações + +- **Cobertura**: 4 cenários OWASP hoje. Outros (auth, crypto, RCE) serão adicionados. +- **Línguas**: exemplos em Python/JS. Em outras linguagens, Claude generaliza mas pode pular detalhes. +- **Audit**: não substitui pentesting nem code review humano. + +ShieldCode reduz o ruído inicial. Defense-in-depth ainda exige outras layers. diff --git a/docs/getting-started/install.md b/docs/getting-started/install.md new file mode 100644 index 0000000..d91f36f --- /dev/null +++ b/docs/getting-started/install.md @@ -0,0 +1,47 @@ +# Instalação + +## Pré-requisitos + +- **Claude Code** ([docs](https://docs.claude.com/en/docs/claude-code)) +- **Bash** ou shell POSIX + +## Instalação + +=== "Clone do repo" + + ```bash + git clone https://github.com/nikolasdehor/shieldcode + cd shieldcode + ./install.sh --scope=user # global + # ou + ./install.sh --scope=project # só nesse projeto + ``` + +=== "One-liner" + + ```bash + curl -sSL https://raw.githubusercontent.com/nikolasdehor/shieldcode/main/install.sh | bash -s -- --scope=user + ``` + +## Verificar + +No Claude Code: + +``` +/skills +``` + +Deve listar `shieldcode`. + +## Desinstalar + +```bash +./uninstall.sh --scope=user +# ou +./uninstall.sh --scope=project +``` + +## Próximos passos + +- [Quickstart](quickstart.md) +- [Como a skill funciona](how.md) diff --git a/docs/getting-started/quickstart.md b/docs/getting-started/quickstart.md new file mode 100644 index 0000000..1fe653e --- /dev/null +++ b/docs/getting-started/quickstart.md @@ -0,0 +1,63 @@ +# Quickstart + +## 1. Instale a skill + +Veja [Instalação](install.md). + +## 2. Comece um projeto + +```bash +cd ~/projects/meu-novo-app +claude +``` + +## 3. Peça código com proteção + +Forma 1 — explicitamente: + +> "Use a skill shieldcode. Escreva um endpoint Flask que recebe email do usuário e salva no DB." + +Forma 2 — implícita (Claude detecta): + +> "Crie um sistema de upload de arquivos em Express" + +(O Claude vai automaticamente puxar a skill por causa do contexto de "upload" + "arquivos" envolver path traversal e validação.) + +## 4. Resultado + +Claude entrega **dois arquivos lado a lado**: + +```python +# vulnerable.py — o que NÃO fazer +@app.route("/upload", methods=["POST"]) +def upload(): + file = request.files["file"] + file.save(f"/var/uploads/{file.filename}") # path traversal! + + +# hardened.py — o jeito seguro +import secrets +from pathlib import Path + +UPLOADS = Path("/var/uploads").resolve() + +@app.route("/upload", methods=["POST"]) +def upload(): + file = request.files["file"] + safe_name = secrets.token_hex(16) + Path(file.filename).suffix + target = (UPLOADS / safe_name).resolve() + if not str(target).startswith(str(UPLOADS) + "/"): + abort(400) + file.save(target) +``` + ++ explicação clara do **porquê** o primeiro é vulnerável. + +## 5. Aprender enquanto codifica + +A skill ensina padrões seguros conforme você codifica. Depois de algumas semanas, você intuitivamente pensa em segurança ao escrever. + +## Próximos passos + +- [Como a skill funciona](how.md) +- [Vulnerabilidades cobertas](../vulns/sql-injection.md) diff --git a/docs/index.html b/docs/index.html deleted file mode 100644 index a28c211..0000000 --- a/docs/index.html +++ /dev/null @@ -1,1270 +0,0 @@ - - - - - - ShieldCode - Stop Shipping Vulnerable Code - - - - - - - - - -
-
- -
-
- 62% of AI-generated code has security vulnerabilities -
- -

Stop Shipping
Vulnerable Code

- -

- Security hardening and error handling expertise for Claude Code. - 62% of AI-generated code has vulnerabilities. - ShieldCode fixes that. -

- - - - -
-
-
-
-
-
- auth.ts -
-
-
-
-
✗ UNSAFE — password hashing
-
const hash = crypto.createHash('md5').update(password).digest('hex');
-
-
✓ SAFE — with ShieldCode
-
const hash = await argon2.hash(password, { type: argon2.argon2id });
-
-
-
✗ UNSAFE — SQL injection
-
db.query(`SELECT * FROM users WHERE email = '${email}'`);
-
-
✓ SAFE — with ShieldCode
-
db.query('SELECT * FROM users WHERE email = $1', [email]);
-
-
-
✗ UNSAFE — error handling
-
catch (e) { res.status(500).json({ error: e.stack }); }
-
-
✓ SAFE — with ShieldCode
-
catch (e) {
  logger.error({ err: e, correlationId });
  res.status(500).json({ error: 'An unexpected error occurred' });
}
-
-
-
✗ UNSAFE — XSS vulnerability
-
element.innerHTML = `<div>${userInput}</div>`;
-
-
✓ SAFE — with ShieldCode
-
element.textContent = userInput;
-
-
-
-
-
-
-
-
-
-
- - -
-
-
-
62%
-
of AI-generated code
contains vulnerabilities
-
-
-
45%
-
fail OWASP Top 10
security checks
-
-
-
2x
-
more error handling gaps
than human-written code
-
-
-
- - -
- -

Everything Claude leaves out.
ShieldCode puts back in.

-

Two pillars of production-grade code, enforced automatically on every file Claude touches.

- -
-
-
-
- -
- Security -
-
    -
  • Input Validation
  • -
  • SQL / NoSQL Injection Prevention
  • -
  • XSS Prevention
  • -
  • Authentication & JWT
  • -
  • Authorization & Access Control
  • -
  • Secrets Management
  • -
  • File Security
  • -
  • Dependency Safety (Slopsquatting)
  • -
-
- -
-
-
- -
- Error Handling -
-
    -
  • Structured Exception Hierarchy
  • -
  • Correct HTTP Status Codes
  • -
  • Secure Logging (no stack traces exposed)
  • -
  • Retry & Circuit Breaker Patterns
  • -
  • Async Error Safety
  • -
  • Safe User-Facing Messages
  • -
-
-
-
- - -
-
- -

What changes when
ShieldCode is active

- -
- -
-
-
- - UNSAFE -
-
-
SQL Injection
-
db.query(`SELECT * FROM users
-  WHERE email = '${email}'`);
-
-
-
-
- - SAFE -
-
-
Parameterized Query
-
db.query(
-  'SELECT * FROM users WHERE email = $1',
-  [email]
-);
-
-
-
- -
-
-
- - UNSAFE -
-
-
Leaking Stack Trace
-
catch (e) {
-  res.status(500).json({
-    error: e.stack
-  });
-}
-
-
-
-
- - SAFE -
-
-
Structured Error Handling
-
catch (e) {
-  logger.error({ err: e, correlationId });
-  res.status(500).json({
-    error: 'An unexpected error occurred'
-  });
-}
-
-
-
- -
-
-
- - UNSAFE -
-
-
Weak Password Hash
-
const hash = crypto
-  .createHash('md5')
-  .update(password)
-  .digest('hex');
-
-
-
-
- - SAFE -
-
-
Argon2id Hashing
-
const hash = await argon2.hash(password, {
-  type: argon2.argon2id,
-  memoryCost: 65536,
-  timeCost: 3
-});
-
-
-
- -
-
-
- - -
- -

Three steps to secure code.
Zero configuration.

- -
-
-
01
-
- -
-
Install the skill
-
One curl command copies the skill to ~/.claude/skills/shieldcode/. Takes under 5 seconds.
-
- -
-
02
-
- -
-
Code as usual
-
Claude Code auto-loads all skills from your skills directory. ShieldCode activates silently on every session, no prompting needed.
-
- -
-
03
-
- -
-
Ship secure code
-
Parameterized queries, argon2 hashing, ownership checks, structured errors. Built in, every time, by default.
-
-
-
- - -
-
-
- -

One command. Done.

-

No config files. No API keys. No plugins to manage.
ShieldCode loads automatically on every Claude Code session.

- -
-
-
-
-
-
-
- $ - curl -fsSL https://raw.githubusercontent.com/nikolasdehor/shieldcode/main/install.sh | bash - -
-
- -

MIT License. Works with Claude Code v0.2+. Supports TypeScript, JavaScript, and Python.

-
-
- - - - - - - diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..5d7d23f --- /dev/null +++ b/docs/index.md @@ -0,0 +1,77 @@ +# ShieldCode + +**Skill do Claude Code que protege código gerado contra OWASP Top 10.** SQL injection, XSS, SSRF, path traversal e mais. + +[![License](https://img.shields.io/badge/license-MIT-22c55e)](https://github.com/nikolasdehor/shieldcode/blob/main/LICENSE) +[![Claude Code](https://img.shields.io/badge/Claude%20Code-skill-7c3aed)](https://docs.claude.com/en/docs/claude-code/skills) +[![OWASP](https://img.shields.io/badge/OWASP-top%2010-e62832)](https://owasp.org/Top10/) + +## Por que ShieldCode + +IA generativa é incrível pra prototipar rápido, mas tem um problema: **modelos de IA tendem a gerar código sem considerar segurança**. Estudo da Stanford mostrou que ~40% de código gerado por LLMs tem vulnerabilidades exploráveis. + +ShieldCode é uma skill para Claude Code que **força o Claude a aplicar padrões seguros** ao gerar código que toca: + +- Banco de dados (parametrize queries) +- Input do usuário (sanitização) +- Requisições externas (validação de URL) +- Sistema de arquivos (canonicalização de paths) +- Renderização HTML (escape automático) + +## Como funciona + +ShieldCode **NÃO é** um pacote npm/pip. É uma skill markdown que o Claude Code lê e aplica: + +```mermaid +flowchart LR + A[Você pede código] --> B[Claude lê skill 'shieldcode'] + B --> C{Toca dados
sensíveis?} + C -->|sim| D[Aplica padrões seguros] + C -->|não| E[Código normal] + D --> F[Resultado: vulnerable.py + hardened.py + explicação] + E --> G[Resultado: código normal] +``` + +## Exemplo + +``` +Você: "faz um endpoint Flask que recebe nome de usuário e busca no DB" + +Sem ShieldCode: +@app.route("/user/") +def get_user(username): + cursor.execute(f"SELECT * FROM users WHERE name='{username}'") # SQL injection! + +Com ShieldCode (Claude lê skill): +@app.route("/user/") +def get_user(username): + cursor.execute("SELECT * FROM users WHERE name = %s", (username,)) # parametrizado + # + explicação no chat sobre por que essa forma é segura +``` + +## Vulnerabilidades cobertas + +| Vulnerabilidade | OWASP | Exemplo no repo | +|----------------|-------|-----------------| +| [SQL Injection](vulns/sql-injection.md) | A03 | `examples/sql-injection/` | +| [XSS](vulns/xss.md) | A03 | `examples/xss/` | +| [SSRF](vulns/ssrf.md) | A10 | `examples/ssrf/` | +| [Path Traversal](vulns/path-traversal.md) | A01 | `examples/path-traversal/` | + +## Quick start + +```bash +git clone https://github.com/nikolasdehor/shieldcode +cd shieldcode +./install.sh --scope=user +``` + +Em qualquer projeto, peça ao Claude: + +> "Use a skill shieldcode quando escrever código que toca DB, input ou rede" + +## Onde ir agora + +[:material-rocket: Instalação](getting-started/install.md){ .md-button .md-button--primary } +[:material-shield: Vulnerabilidades cobertas](vulns/sql-injection.md){ .md-button } +[:material-github: GitHub](https://github.com/nikolasdehor/shieldcode){ .md-button } diff --git a/docs/vulns/path-traversal.md b/docs/vulns/path-traversal.md new file mode 100644 index 0000000..5a210a6 --- /dev/null +++ b/docs/vulns/path-traversal.md @@ -0,0 +1,98 @@ +# Path Traversal + +OWASP A01:2021 (Broken Access Control). Atacante usa `../` para escapar do diretório esperado e acessar arquivos arbitrários. + +## Vulnerável + +```python +# VULNERÁVEL +@app.route("/download/") +def download(filename): + return send_file(f"/var/uploads/{filename}") +``` + +Atacante envia `?filename=../../../etc/passwd` e baixa o arquivo do sistema. + +## Padrão seguro: canonicalizar + validar + +```python +from pathlib import Path +from flask import abort + +UPLOADS = Path("/var/uploads").resolve() + +@app.route("/download/") +def download(filename): + target = (UPLOADS / filename).resolve() + + # Garante que target está DENTRO de UPLOADS + try: + target.relative_to(UPLOADS) + except ValueError: + abort(403) + + if not target.is_file(): + abort(404) + + return send_file(target) +``` + +`Path.resolve()` normaliza `../` para o caminho real. Depois `relative_to` confirma que esse caminho real está dentro do diretório esperado. + +## Variantes em outras linguagens + +=== "Node.js" + + ```javascript + const path = require("path"); + const UPLOADS = path.resolve("/var/uploads"); + + app.get("/download/:filename", (req, res) => { + const target = path.resolve(UPLOADS, req.params.filename); + if (!target.startsWith(UPLOADS + path.sep)) { + return res.status(403).end(); + } + res.sendFile(target); + }); + ``` + +=== "Go" + + ```go + func download(w http.ResponseWriter, r *http.Request) { + filename := r.URL.Query().Get("filename") + target := filepath.Join(uploadsDir, filename) + target, err := filepath.Abs(target) + if err != nil { + http.Error(w, "bad path", 400) + return + } + if !strings.HasPrefix(target, uploadsDir + string(os.PathSeparator)) { + http.Error(w, "forbidden", 403) + return + } + http.ServeFile(w, r, target) + } + ``` + +## Cuidados adicionais + +- **Renomeie uploads**: ao receber upload, salve com nome aleatório (`secrets.token_hex()`), não confie no nome do cliente +- **Whitelist extensions**: `if not filename.endswith((".png", ".jpg", ".pdf")): abort(400)` +- **Permissões de filesystem**: process do app não deve ter permissão de leitura fora de `/var/uploads/` +- **Cuidado com symlinks**: `Path.resolve()` segue symlinks. Em diretórios sensíveis, valide isso explicitamente + +## Exemplo no repo + +[examples/path-traversal/](https://github.com/nikolasdehor/shieldcode/tree/main/examples/path-traversal) + +## Como ShieldCode previne + +Skill instrui Claude a: + +1. NUNCA usar concatenação direta de string em paths +2. SEMPRE canonicalizar com `Path.resolve()` / `path.resolve()` / `filepath.Abs()` +3. SEMPRE validar que path resolvido começa com diretório esperado +4. Sugerir renomear uploads pra evitar confiar em filename do cliente +5. Sugerir whitelist de extensions quando contextualmente possível +6. Alertar sobre symlinks em diretórios sensíveis diff --git a/docs/vulns/sql-injection.md b/docs/vulns/sql-injection.md new file mode 100644 index 0000000..4ed62ac --- /dev/null +++ b/docs/vulns/sql-injection.md @@ -0,0 +1,98 @@ +# SQL Injection + +OWASP A03:2021 (Injection). A vulnerabilidade mais antiga e ainda mais explorada da web. + +## Como acontece + +Concatenação de input do usuário em query SQL: + +```python +# VULNERÁVEL +@app.route("/search") +def search(): + name = request.args.get("name") + cursor.execute(f"SELECT * FROM users WHERE name='{name}'") +``` + +Input `' OR '1'='1` retorna todos os usuários. Input `'; DROP TABLE users; --` apaga a tabela. + +## Padrão seguro (parametrização) + +```python +# SEGURO +@app.route("/search") +def search(): + name = request.args.get("name") + cursor.execute("SELECT * FROM users WHERE name = %s", (name,)) +``` + +O driver do banco escapa o valor antes de enviar. Input malicioso vira string literal, sem efeito. + +## Variantes em outras linguagens + +=== "Node.js (pg)" + + ```javascript + // VULNERÁVEL + db.query(`SELECT * FROM users WHERE name='${name}'`); + + // SEGURO + db.query("SELECT * FROM users WHERE name = $1", [name]); + ``` + +=== "Go (database/sql)" + + ```go + // VULNERÁVEL + db.Query(fmt.Sprintf("SELECT * FROM users WHERE name='%s'", name)) + + // SEGURO + db.Query("SELECT * FROM users WHERE name = ?", name) + ``` + +=== "Java (PreparedStatement)" + + ```java + // VULNERÁVEL + Statement stmt = conn.createStatement(); + stmt.executeQuery("SELECT * FROM users WHERE name='" + name + "'"); + + // SEGURO + PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE name = ?"); + ps.setString(1, name); + ps.executeQuery(); + ``` + +## Por que `eval` SQL acontece tanto + +ORMs amenizam o problema, mas em "queries cruas" (raw queries) o desenvolvedor volta a concatenar. Pesquisas tipo "WHERE LIKE %x%" tentam parametrizar wildcards e quebram. Solução: parametrize a string base, deixe wildcards fora: + +```python +# SEGURO +cursor.execute( + "SELECT * FROM users WHERE name LIKE %s", + (f"%{name}%",) # wildcards fora da query, dentro do parâmetro +) +``` + +## Exemplo no repo + +[examples/sql-injection/](https://github.com/nikolasdehor/shieldcode/tree/main/examples/sql-injection) tem `vulnerable.py` e `hardened.py` lado a lado. + +## Defenses extras + +- **ORM** quando possível (Django ORM, SQLAlchemy, Prisma) +- **Least privilege**: usuário do DB com SELECT só onde precisa +- **WAF**: bloqueia padrões óbvios em produção +- **Logging**: log queries para forense +- **Pentest periódico** + +## Como ShieldCode previne + +A skill instrui o Claude a: + +1. NUNCA concatenar input em SQL +2. SEMPRE usar `?`/`$1`/`%s` placeholders +3. Aplicar `LIKE` com wildcards dentro do parâmetro +4. Validar tipos antes de mandar pra query +5. Quando o usuário pedir "query raw", explicar o risco e ainda parametrizar diff --git a/docs/vulns/ssrf.md b/docs/vulns/ssrf.md new file mode 100644 index 0000000..aa9e3ce --- /dev/null +++ b/docs/vulns/ssrf.md @@ -0,0 +1,82 @@ +# SSRF (Server-Side Request Forgery) + +OWASP A10:2021. Atacante faz seu servidor requisitar URLs que ele controla, expondo recursos internos. + +## Como acontece + +App permite que usuário forneça URL para "preview" / "thumbnail" / "webhook": + +```python +# VULNERÁVEL +@app.route("/preview") +def preview(): + url = request.args.get("url") + response = requests.get(url) # qualquer URL! + return response.text +``` + +Atacante envia: + +- `http://169.254.169.254/latest/meta-data/` → metadata de cloud (AWS/GCP/Azure) +- `http://localhost:6379/INFO` → consulta Redis interno +- `http://internal-admin.local/users` → endpoint admin +- `file:///etc/passwd` → arquivos locais + +## Padrão seguro: allowlist + validação + +```python +import ipaddress +from urllib.parse import urlparse +import socket + +ALLOWED_HOSTS = {"example.com", "trusted-cdn.com"} + +def fetch_safe(url: str) -> str: + parsed = urlparse(url) + + # 1. Só HTTP(S) + if parsed.scheme not in ("http", "https"): + raise ValueError("Schema inválido") + + # 2. Host na allowlist + if parsed.hostname not in ALLOWED_HOSTS: + raise ValueError("Host não permitido") + + # 3. Resolver DNS e validar IP (evita DNS rebinding) + ip = socket.gethostbyname(parsed.hostname) + ip_obj = ipaddress.ip_address(ip) + + if ip_obj.is_private or ip_obj.is_loopback or ip_obj.is_link_local: + raise ValueError("IP interno bloqueado") + + # 4. Faz request com timeout e sem follow_redirects (atacante pode redirecionar) + response = requests.get(url, timeout=5, allow_redirects=False) + return response.text +``` + +## Se NÃO precisar de allowlist + +Quando o app é tipo "webhook delivery" (usuário cadastra URL pra receber notificações), allowlist não rola. Mitigações: + +- Bloquear IPs privados (RFC 1918) +- Bloquear loopback e link-local +- Bloquear AWS metadata IP (`169.254.169.254`) +- Timeout curto +- Sem follow_redirects (ou validar destino) +- Rede isolada (egress firewall que só permite saída pra internet pública) + +## Exemplo no repo + +[examples/ssrf/](https://github.com/nikolasdehor/shieldcode/tree/main/examples/ssrf) + +## Como ShieldCode previne + +Skill instrui Claude a: + +1. Identificar quando código faz request HTTP a URL fornecida por usuário +2. SEMPRE validar schema (`http`/`https` apenas) +3. SEMPRE resolver DNS e validar que IP não é interno +4. Bloquear `169.254.169.254` e RFC 1918 explicitamente +5. Timeout curto (5-10s) +6. `allow_redirects=False` (ou validar destino) +7. Em ambientes cloud, alertar sobre risco de IMDSv1 (use IMDSv2) diff --git a/docs/vulns/xss.md b/docs/vulns/xss.md new file mode 100644 index 0000000..39389b4 --- /dev/null +++ b/docs/vulns/xss.md @@ -0,0 +1,96 @@ +# XSS (Cross-Site Scripting) + +OWASP A03:2021 (Injection). Atacante injeta JavaScript que executa no browser de outros usuários. + +## Tipos + +- **Reflected**: input vai direto na resposta sem sanitização +- **Stored**: input vai pro DB e depois é renderizado pra todos +- **DOM-based**: JavaScript do próprio app constrói HTML inseguro + +## Vulnerável + +```python +# Reflected XSS +@app.route("/search") +def search(): + q = request.args.get("q") + return f"

Você buscou por: {q}

" +``` + +Input `` rouba token de qualquer usuário que clique no link. + +## Padrão seguro: auto-escape + +Use template engine que escapa por padrão: + +```python +# Flask + Jinja2 (auto-escape ON por default) +@app.route("/search") +def search(): + q = request.args.get("q") + return render_template("search.html", q=q) +``` + +Template: + +```html +

Você buscou por: {{ q }}

+``` + +Jinja escapa `<`, `>`, `&`, `"` por padrão. + +## Client-side: nunca usar HTML cru com input + +```javascript +// VULNERÁVEL +element.innerHTML = userInput; + +// SEGURO +element.textContent = userInput; +``` + +Em React, Vue, Svelte: passe como prop normal, framework escapa automaticamente. + +## Quando precisa renderizar HTML do usuário + +Caso real: editor de markdown. Use sanitizer: + +```python +import bleach + +safe_html = bleach.clean( + user_html, + tags=["p", "strong", "em", "a", "ul", "ol", "li", "code", "pre"], + attributes={"a": ["href", "title"]}, + protocols=["http", "https", "mailto"], +) +``` + +Em JS, use DOMPurify com allowlist conservadora. + +## Content Security Policy + +Layer adicional. Mesmo se XSS acontecer, CSP bloqueia execução: + +```text +Content-Security-Policy: + default-src 'self'; + script-src 'self' 'nonce-RANDOM'; + style-src 'self' 'unsafe-inline'; +``` + +Sem inline scripts arbitrários, payloads injetados não rodam. + +## Exemplo no repo + +[examples/xss/](https://github.com/nikolasdehor/shieldcode/tree/main/examples/xss) + +## Como ShieldCode previne + +Skill instrui Claude a: + +1. SEMPRE usar template engine com auto-escape +2. NUNCA usar atribuição direta de HTML cru com input +3. Para HTML do usuário, sugerir sanitizer com whitelist conservadora +4. Sugerir CSP no servidor diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..139ee3c --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,109 @@ +site_name: ShieldCode +site_description: Skill do Claude Code que protege código gerado contra OWASP Top 10. SQL injection, XSS, SSRF, path traversal e mais. +site_url: https://nikolasdehor.github.io/shieldcode/ +site_author: Nikolas de Hor +repo_url: https://github.com/nikolasdehor/shieldcode +repo_name: nikolasdehor/shieldcode +edit_uri: edit/main/docs/ + +theme: + name: material + language: pt-BR + palette: + - media: "(prefers-color-scheme: light)" + scheme: default + primary: red + accent: deep orange + toggle: + icon: material/weather-night + name: Modo escuro + - media: "(prefers-color-scheme: dark)" + scheme: slate + primary: red + accent: deep orange + toggle: + icon: material/weather-sunny + name: Modo claro + features: + - navigation.tabs + - navigation.sections + - navigation.expand + - navigation.top + - navigation.tracking + - search.highlight + - search.suggest + - content.code.copy + - content.action.edit + - content.tabs.link + icon: + repo: fontawesome/brands/github + +extra: + social: + - icon: fontawesome/brands/github + link: https://github.com/nikolasdehor + - icon: fontawesome/brands/linkedin + link: https://www.linkedin.com/in/nikolasdehor + generator: false + +markdown_extensions: + - admonition + - attr_list + - def_list + - footnotes + - md_in_html + - tables + - toc: + permalink: true + - pymdownx.details + - pymdownx.highlight: + anchor_linenums: true + line_spans: __span + pygments_lang_class: true + - pymdownx.inlinehilite + - pymdownx.snippets: + base_path: [".", "docs"] + check_paths: true + - pymdownx.superfences: + custom_fences: + - name: mermaid + class: mermaid + format: !!python/name:pymdownx.superfences.fence_code_format + - pymdownx.tabbed: + alternate_style: true + - pymdownx.tasklist: + custom_checkbox: true + - pymdownx.emoji: + emoji_index: !!python/name:material.extensions.emoji.twemoji + emoji_generator: !!python/name:material.extensions.emoji.to_svg + +plugins: + - search: + lang: pt + - mermaid2 + - git-revision-date-localized: + enable_creation_date: true + type: timeago + timezone: America/Sao_Paulo + locale: pt + fallback_to_build_date: true + +nav: + - Início: index.md + - Comece aqui: + - Instalação: getting-started/install.md + - Quickstart: getting-started/quickstart.md + - Como a skill funciona: getting-started/how.md + - Vulnerabilidades cobertas: + - SQL Injection: vulns/sql-injection.md + - XSS: vulns/xss.md + - SSRF: vulns/ssrf.md + - Path Traversal: vulns/path-traversal.md + - Integração com frameworks: + - Django: frameworks/django.md + - FastAPI: frameworks/fastapi.md + - Express / Node: frameworks/express.md + - Spring Boot: frameworks/spring.md + - Comparativo: comparison.md + - Contribuir: contributing.md + - Changelog: changelog.md