# CWE Catalog — ns-flow-kit (seed universal OWASP Top 10)

> **O que é:** catálogo institucional de como cada classe CWE se manifesta. Esta versão é a **seed universal** distribuída pelo kit — cobre OWASP Top 10 (2021) com padrões genéricos.
>
> **Personalização por projeto:** rode `/bootstrap-security --stack <stack>` para enriquecer cada entrada com paths e utilitários específicos do projeto.
>
> **Atualizado por:** `learning-agent` ao fechar tickets de vulnerabilidade (`vulnerability-flow`).

## Como ler / como contribuir

Cada entrada segue 6 seções. Quando uma classe CWE for fechada num ticket, o `learning-agent`:

- **Cria entrada** se ainda não existir (gap 4.7.2 da PoC original).
- **Acrescenta ticket à seção "Histórico"** se a entrada já existe.

---

## CWE-79 / CWE-80 — Cross-site Scripting (Reflected / Stored)

### Onde mora
- Views/templates que recebem input do usuário e usam funções "raw" sem encoding
- APIs que ecoam input no payload de resposta
- Logs/audit trails que misturam input do usuário com markup

### Padrão vulnerável (genérico)
```
{linguagem}
// Razor (.NET):   @Html.Raw(model.UserInput)
// JSP (Java):     ${userInput}  ou <%= request.getParameter("x") %>
// PHP:            echo $_GET['x'];
// Node/React:     dangerouslySetInnerHTML={{ __html: req.body.html }}
// Python/Django:  {{ user_input|safe }}
```

### Padrão seguro (referência)
```
{linguagem}
// Razor:  @Html.DisplayFor(m => m.UserInput)  (encoding default)
// JSP:    <c:out value="${userInput}"/>
// PHP:    echo htmlspecialchars($_GET['x'], ENT_QUOTES, 'UTF-8');
// React:  {userInput}  (escape automático pelo JSX)
// Django: {{ user_input }}  (escape default; nunca use |safe sem sanitização)
```

### ADR/Policy relacionada
- `.claude/policies/security-policy.md` — output encoding obrigatório
- `.claude/policies/vulnerability-policy.md` — Regra 2 (anti-pattern check)

### Falsos-positivos comuns
- Scanners flagram `String.Format` ou template literals que **não** chegam a output HTML
- Markdown renderers com sanitizer pré-built (DOMPurify, SafeBrowsing) escapam por design

### Histórico
*(preenchido pelo learning-agent)*

---

## CWE-89 — SQL Injection

### Onde mora
- DAOs / repositories que concatenam strings para montar SQL
- Stored procedures com EXEC dinâmico
- ORMs em modo "raw query" sem parâmetros

### Padrão vulnerável
```
// .NET (ADO.NET):
var cmd = new SqlCommand("SELECT * FROM Users WHERE Name = '" + name + "'", conn);

// Java (JDBC):
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery("SELECT * FROM Users WHERE Name = '" + name + "'");

// PHP (mysqli):
$result = mysqli_query($conn, "SELECT * FROM Users WHERE Name = '$name'");

// Python (psycopg2):
cursor.execute("SELECT * FROM users WHERE name = '%s'" % name)
```

### Padrão seguro
```
// .NET:
var cmd = new SqlCommand("SELECT * FROM Users WHERE Name = @name", conn);
cmd.Parameters.AddWithValue("@name", name);

// Java:
PreparedStatement st = conn.prepareStatement("SELECT * FROM Users WHERE Name = ?");
st.setString(1, name);

// PHP:
$stmt = $conn->prepare("SELECT * FROM Users WHERE Name = ?");
$stmt->bind_param("s", $name);

// Python:
cursor.execute("SELECT * FROM users WHERE name = %s", (name,))
```

### ADR/Policy relacionada
- `.claude/policies/security-policy.md` — "SQL parametrizado obrigatório"

### Falsos-positivos comuns
- Concat em strings de UI builder ou debug log (não chega a `cmd.CommandText`)
- ORMs usando query DSL (Linq, JPA Criteria, Eloquent) — não são `raw SQL`

### Histórico
*(preenchido pelo learning-agent)*

---

## CWE-200 / CWE-209 — Information Exposure / Error Disclosure

### Onde mora
- Páginas de erro padrão expondo stack trace
- API responses com `exception.message` literal
- Logs/headers vazando metadados (versão de framework, paths internos, conn strings)

### Padrão vulnerável
```
// .NET (Web.config):
<customErrors mode="Off" />
<compilation debug="true" />

// Java (Spring):
@ExceptionHandler
public String handle(Exception e) {
    return "Error: " + e.toString();  // expõe stack
}

// Node/Express:
app.use((err, req, res, next) => {
    res.status(500).send(err.stack);   // expõe stack
});
```

### Padrão seguro
```
// .NET:
<customErrors mode="On" defaultRedirect="~/Error" />
<compilation debug="false" />

// Java/Spring:
@ExceptionHandler
public String handle(Exception e) {
    logger.error("Erro processando", e);  // log interno
    return "Ocorreu um erro. Código: " + correlationId;  // sem detalhes
}

// Node:
app.use((err, req, res, next) => {
    logger.error(err);
    res.status(500).send({ error: 'Internal error', correlationId });
});
```

### ADR/Policy relacionada
- `.claude/policies/security-policy.md` — "Sanitização de erros expostos"

### Falsos-positivos comuns
- Linha `<customErrors mode="Off">` comentada
- Stack trace exposto apenas em ambiente de Dev (controlado por env var)

### Histórico
*(preenchido pelo learning-agent)*

---

## CWE-285 / CWE-287 — Improper Authorization / Authentication

### Onde mora
- Controllers/endpoints sem decorator `[Authorize]`/`@PreAuthorize`/middleware
- Lógica de AuthZ implementada manualmente (sem usar framework)
- Comparações de role com string igualdade (case-sensitive)

### Padrão vulnerável
```
// .NET MVC:
public class AdminController : Controller {
    public ActionResult Delete(int id) { ... }  // sem [Authorize]
}

// Spring:
@RestController
public class AdminController {
    @DeleteMapping("/users/{id}")
    public void delete(@PathVariable Long id) { ... }  // sem @PreAuthorize
}

// Express:
app.delete('/users/:id', (req, res) => { ... });  // sem middleware auth
```

### Padrão seguro
```
// .NET:
[Authorize(Roles = "Admin")]
public class AdminController : Controller { ... }

// Spring:
@PreAuthorize("hasRole('ADMIN')")
public void delete(@PathVariable Long id) { ... }

// Express:
app.delete('/users/:id', requireAuth, requireRole('admin'), (req, res) => { ... });
```

### ADR/Policy relacionada
- `.claude/policies/security-policy.md` — defaults de AuthZ
- `.claude/policies/vulnerability-policy.md` — Regra 7 (dupla aprovação)

### Falsos-positivos comuns
- Controllers com `[AllowAnonymous]` explícito (público por design)
- Endpoints públicos documentados (saúde, ping, version)

### Histórico
*(preenchido pelo learning-agent)*

---

## CWE-352 — Cross-Site Request Forgery (CSRF)

### Onde mora
- Forms HTML sem token CSRF
- Endpoints POST/DELETE sem validação de token/Origin/SameSite
- APIs cookie-auth sem `SameSite=Strict` ou `SameSite=Lax`

### Padrão vulnerável
```
// .NET MVC:
[HttpPost]
public ActionResult DeleteAccount(int id) { ... }   // sem [ValidateAntiForgeryToken]

// Spring (config global):
http.csrf().disable();                              // explicitamente desabilitado

// Express:
app.use(cookieParser());                            // sem csurf middleware
```

### Padrão seguro
```
// .NET:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult DeleteAccount(int id) { ... }

// Spring:
http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());

// Express:
const csrf = require('csurf');
app.use(csrf({ cookie: true }));
```

### ADR/Policy relacionada
- `.claude/policies/security-policy.md` — proteção CSRF obrigatória em mutações
- `.claude/policies/vulnerability-policy.md` — Wave 5 (design)

### Falsos-positivos comuns
- APIs JWT-only (sem cookie auth) — não precisa CSRF se token é em header
- GET endpoints (CSRF protege mutações, não leituras)

### Histórico
*(preenchido pelo learning-agent)*

---

## CWE-502 — Insecure Deserialization

### Onde mora
- `BinaryFormatter` em .NET (deprecated)
- `pickle.loads` em Python
- `unserialize` em PHP
- `JsonConvert.DeserializeObject` com `TypeNameHandling.All`
- `node-serialize` em Node

### Padrão vulnerável
```
// .NET:
var data = (Order)new BinaryFormatter().Deserialize(stream);

// Python:
order = pickle.loads(request.body)

// PHP:
$order = unserialize($_POST['data']);
```

### Padrão seguro
```
// .NET:  System.Text.Json com tipos explícitos
var order = JsonSerializer.Deserialize<Order>(json);

// Python:  JSON ou marshmallow/pydantic com schema
order = OrderSchema().load(json.loads(request.body))

// PHP:    json_decode (não unserialize)
$order = json_decode($_POST['data'], true);
```

### ADR/Policy relacionada
- `.claude/policies/security-policy.md` — "BinaryFormatter proibido"

### Falsos-positivos comuns
- Deserialização de fonte interna confiável (DB, cache próprio) — ainda assim risco em supply chain

### Histórico
*(preenchido pelo learning-agent)*

---

## CWE-524 / CWE-614 — Sensitive Cookie / Missing HSTS

### Onde mora
- Cookies de sessão sem `HttpOnly` / `Secure`
- Ausência de `Strict-Transport-Security` no header
- `requireSSL="false"` no Web.config

### Padrão vulnerável
```xml
<!-- .NET Web.config -->
<httpCookies httpOnlyCookies="false" requireSSL="false" />
```

### Padrão seguro
```xml
<!-- .NET Web.config -->
<httpCookies httpOnlyCookies="true" requireSSL="true" sameSite="Strict" />

<!-- + custom header HSTS via outboundRules -->
<rewrite>
  <outboundRules>
    <rule name="HSTS">
      <match serverVariable="RESPONSE_Strict_Transport_Security" pattern=".*" />
      <action type="Rewrite" value="max-age=31536000; includeSubDomains" />
    </rule>
  </outboundRules>
</rewrite>
```

### ADR/Policy relacionada
- `.claude/policies/security-policy.md` — cookie flags + HSTS obrigatório em produção

### Falsos-positivos comuns
- Cookie analytics third-party (não-session) sem `HttpOnly` por design

### Histórico
*(preenchido pelo learning-agent)*

---

## CWE-798 — Use of Hard-coded Credentials

### Onde mora
- Connection strings em código (`"Server=...;User=sa;Password=..."`)
- API keys em código (`const apiKey = "AKIA..."`)
- Tokens em testes de integração não-segregados

### Padrão vulnerável
```
// .NET:
private const string ConnStr = "Server=prod-db;User=sa;Password=Admin@123";

// Python:
API_KEY = "sk-abc123def456"

// Node:
const dbConn = mysql.createConnection({ password: 'admin123' });
```

### Padrão seguro
```
// .NET:  ConfigurationManager + Azure Key Vault / AWS Secrets Manager
var connStr = ConfigurationManager.ConnectionStrings["Prod"].ConnectionString;

// Python:  env vars + python-dotenv
import os
API_KEY = os.environ["API_KEY"]

// Node:  process.env + secrets manager
const dbConn = mysql.createConnection({ password: process.env.DB_PASSWORD });
```

### ADR/Policy relacionada
- `.claude/policies/security-policy.md` — "Zero credenciais hardcoded"

### Falsos-positivos comuns
- `const string SessionKey = "MenuRamos"` flagrado como CWE-798 — é prefix de cache, não credencial. Status `dismissed-fp`.
- Senhas em fixtures de teste isoladas (`tests/fixtures/`) — verificar se não vazam em prod build.

### Histórico
*(preenchido pelo learning-agent)*

---

## CWE-918 — Server-Side Request Forgery (SSRF)

### Onde mora
- Endpoints que aceitam URL como input e fazem fetch
- Webhooks/callbacks com URL controlada pelo usuário
- Proxies / image resizers / link preview

### Padrão vulnerável
```
// .NET:
var resp = httpClient.GetAsync(request.Url);  // sem validação

// Node:
const resp = await fetch(req.body.url);

// Python:
resp = requests.get(request.GET['url'])
```

### Padrão seguro
- Allowlist de hosts/domínios
- Resolver DNS antes do request, validar IP não-privado (sem 169.254.0.0/16 e 10.0.0.0/8)
- Usar serviços tipo Nemean/SSRFmap em CI para validar

### ADR/Policy relacionada
- `.claude/policies/security-policy.md` — validação de URLs externas

### Histórico
*(preenchido pelo learning-agent)*

---

## OWASP-A05 — Security Misconfiguration

### Onde mora
- `Web.config` / `appsettings.json` / `application.yml` em prod com flags de dev
- Headers de segurança ausentes (CSP, X-Frame-Options, Referrer-Policy)
- Diretórios listáveis no servidor web

### Padrão vulnerável
```xml
<!-- .NET Web.config (prod) -->
<compilation debug="true" />
<customErrors mode="Off" />
<system.web>
  <trace enabled="true" />
</system.web>
```

### Padrão seguro
```xml
<compilation debug="false" />
<customErrors mode="On" defaultRedirect="~/Error" />
<system.web>
  <trace enabled="false" />
</system.web>
```

+ Configurar `Web.{CIA}.config` transforms para cada ambiente em projetos multi-CIA.

### ADR/Policy relacionada
- `.claude/policies/security-policy.md` — config drift checks

### Falsos-positivos comuns
- `debug="true"` em `Web.Debug.config` (transform de dev, nunca aplicado em prod build)

### Histórico
*(preenchido pelo learning-agent)*

---

## Falsos-positivos por scanner (global — herdados pelo vuln-triage)

### Semgrep — regras com over-match conhecido

| Regra | Causa do FP | Calibração |
|-------|-------------|-----------|
| `csharp-sql-injection-concat` | Pattern casa concat genérico não-SQL | Restringir taint sink a `SqlCommand.CommandText` |
| `csharp-xss-response-write-userinput` | Casa `StreamWriter.Write` indevidamente | Restringir a `HttpResponse.Write` |
| `csharp-authenticated-action-without-nocache` | Não detecta atributo em nível de classe | Inspecionar `[NoCacheFilter]` herdado |
| `webconfig-debug-true` / `webconfig-customerrors-off` | Linha comentada | `pattern-not-inside: <!-- ... -->` |
| `csharp-path-traversal` | Não inspeciona guard `StartsWith(allowed)` | `pattern-not` com guard |

### Veracode — padrões da plataforma

| Padrão | Caso típico | Tratamento |
|---|---|---|
| `const string SessionKey/CacheKey/LogPrefix` flagrado como CWE-798 | Prefix de cache, não credencial | Status `dismissed-fp`. Marcar "Mitigated by Design" no Veracode UI |
| Path traversal sem inspeção do guard | Guard `StartsWith(allowed)` precede `File.Open` | Status `dismissed-fp` se confirmado manualmente |
| Findings OPEN pós-merge (lag 24h–7d) | Veracode flagra commit já mergeado | Status `closed-via-commit-{SHA}` |

## Métricas do catálogo

- Total de entradas (seed): 11 (OWASP Top 10 + extras: 79/80, 89, 200/209, 285/287, 352, 502, 524/614, 798, 918, A05)
- Última atualização: 2026-05-10 (seed v1.2.0)
- Tickets cobertos no histórico: 0 (kit recém-instalado)
