{
  "$id": "https://codafort.dev/schemas/coda-iast-v1.schema.json",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "definitions": {
    "Confirmation": {
      "description": "Veredito de um finding estático do `codafort` à luz do que o tráfego mostrou\n(RF-470) — o entregável de valor do IAST, e o que o atestado consome (IT-12).",
      "oneOf": [
        {
          "const": "confirmed-at-runtime",
          "description": "O fluxo source→sink foi **observado acontecendo**. Evidência dinâmica: FP ~0.",
          "type": "string"
        },
        {
          "const": "sanitized-at-runtime",
          "description": "O sink foi alcançado, mas com sanitizador da política no caminho.",
          "type": "string"
        },
        {
          "const": "unreached",
          "description": "O código do finding não foi exercitado pelo tráfego. **Não é \"seguro\"** — é\n\"não medido\": a distinção que separa laudo honesto de laudo otimista.",
          "type": "string"
        }
      ]
    },
    "ConfirmationEntry": {
      "description": "Veredito de runtime sobre UM finding estático (RF-470). O laudo lista **todos** os\nfindings estáticos, não só os alcançados — é o que separa \"confirmei 3\" de \"confirmei\n3 de 12\", e o segundo é o único número que o atestado pode usar.",
      "properties": {
        "evidence": {
          "description": "Como o veredito foi alcançado — a rota que exercitou (ou a ausência dela).",
          "type": "string"
        },
        "file": {
          "type": "string"
        },
        "line": {
          "format": "uint32",
          "minimum": 0,
          "type": [
            "integer",
            "null"
          ]
        },
        "rule": {
          "type": "string"
        },
        "static_finding_id": {
          "description": "ID do finding estático (`SF-n` quando veio da sessão do `codafort`).",
          "type": "string"
        },
        "verdict": {
          "$ref": "#/definitions/Confirmation"
        }
      },
      "required": [
        "static_finding_id",
        "rule",
        "file",
        "verdict",
        "evidence"
      ],
      "type": "object"
    },
    "Finding": {
      "description": "Um finding do momento IAST. Converge para o Finding canônico do PRD §4.3; `moment` é\nsempre `run` (evidência dinâmica). Ordenável para emissão determinística.",
      "properties": {
        "agent": {
          "default": "",
          "description": "Runtime que OBSERVOU o fluxo (`python`, `node`, `jvm`). Campo explícito porque a\nalternativa — inferir do texto da evidência — é frágil por construção: foi\nexatamente assim que a régua do IT-09 atribuiu eventos da JVM ao agente Python e\nos contou como falso negativo. Dado que o laudo tem, o laudo declara.",
          "type": "string"
        },
        "confirmation": {
          "anyOf": [
            {
              "$ref": "#/definitions/Confirmation"
            },
            {
              "type": "null"
            }
          ],
          "description": "Veredito de confirmação (RF-470), quando o laudo cruza estático×dinâmico (IT-05)."
        },
        "cwe": {
          "description": "CWE primário da categoria do sink.",
          "format": "uint32",
          "minimum": 0,
          "type": "integer"
        },
        "evidence": {
          "description": "Evidência **redigida** do que foi observado (nunca token/cookie — IT-02).",
          "type": "string"
        },
        "file": {
          "description": "Arquivo do sink observado.",
          "type": "string"
        },
        "line": {
          "description": "Linha do sink, quando o runtime expõe.",
          "format": "uint32",
          "minimum": 0,
          "type": [
            "integer",
            "null"
          ]
        },
        "message": {
          "description": "Explicação humana — nenhum finding sem explicação (PRD §5.4).",
          "type": "string"
        },
        "moment": {
          "description": "Sempre `\"run\"`.",
          "type": "string"
        },
        "route": {
          "default": "",
          "description": "Rota que estava sendo atendida. **Campo declarado, não texto:** a régua do benchmark\nextraía isto da `message` com `rsplit_once(' ')` e, com o laudo correlacionado (o uso\nreal), o último token deixava de ser a rota — recall ia a 0.000 com FN inventados.\nMesmo erro que o `agent` já tinha sofrido. Dado que o laudo tem, o laudo declara.",
          "type": "string"
        },
        "rule": {
          "description": "Regra/identidade do fluxo observado (ex.: `IAST-COMMAND-INJECTION`).",
          "type": "string"
        },
        "sanitizer": {
          "description": "Sanitizador VÁLIDO observado no caminho, quando houve (validado contra a política).\n`None` = fluxo sem defesa. Também era inferido por substring na `message`/`evidence`.",
          "type": [
            "string",
            "null"
          ]
        },
        "severity": {
          "description": "Escala canônica do `coda-finding/1`.",
          "type": "string"
        },
        "static_finding_id": {
          "description": "Finding estático que este confirma, quando correlacionado (IT-02).",
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "rule",
        "moment",
        "severity",
        "file",
        "cwe",
        "message"
      ],
      "type": "object"
    },
    "Tool": {
      "description": "Proveniência da ferramenta emissora.",
      "properties": {
        "name": {
          "type": "string"
        },
        "version": {
          "type": "string"
        }
      },
      "required": [
        "name",
        "version"
      ],
      "type": "object"
    }
  },
  "description": "Envelope de laudo IAST. **Determinístico na emissão** (RF-490): findings ordenados,\n`BTreeMap` nos metadados, nada de iteração de `HashMap` na saída.",
  "properties": {
    "confirmations": {
      "description": "Veredito por finding estático (RF-470). Vazio quando o laudo roda sem `--static`.",
      "items": {
        "$ref": "#/definitions/ConfirmationEntry"
      },
      "type": "array"
    },
    "findings": {
      "default": [],
      "description": "Findings observados. Vazio até o coletor rodar (IT-01+).",
      "items": {
        "$ref": "#/definitions/Finding"
      },
      "type": "array"
    },
    "meta": {
      "additionalProperties": {
        "type": "string"
      },
      "default": {},
      "description": "Metadados determinísticos do run (chave→valor ordenado).",
      "type": "object"
    },
    "schema": {
      "description": "Sempre `coda-iast/1`. Envelope com outro schema é rejeitado na leitura.",
      "type": "string"
    },
    "tool": {
      "$ref": "#/definitions/Tool"
    }
  },
  "required": [
    "schema",
    "tool"
  ],
  "title": "IastEnvelope",
  "type": "object"
}
