tiss-hash
v0.1.0
Published
Hash MD5 do epílogo XML TISS/ANS (Padrão TISS). Reverse-engineered, validado contra goldens reais privados + vetores sintéticos.
Maintainers
Readme
tiss-hash (Node.js)
Calcula a "impressão digital" do trecho final de um documento TISS/ANS. Antes do código, os termos essenciais:
- XML: formato de arquivo de texto que organiza dados em etiquetas (tags) aninhadas, como caixas dentro de caixas. O Padrão TISS é o XML que operadoras de saúde e consultórios usam no Brasil para trocar dados de atendimento (regulamentado pela Agência Nacional de Saúde Suplementar, a ANS).
- Hash: sequência curta e fixa de caracteres calculada a partir de um texto, como uma impressão digital. Mude uma letra, o hash muda inteiro.
- MD5: a receita (algoritmo) que gera o hash; sempre 32 caracteres
hexadecimais (
0-9ea-f). - Epílogo: a parte final do documento TISS, a etiqueta
<ans:hash>, onde o hash precisa ser gravado. - Byte: a menor unidade de dado do computador; um arquivo de texto é uma fila de bytes.
Em uma frase: você passa os bytes de um XML TISS e recebe os 32 caracteres do
hash. Este é o port Node.js ("port" = a mesma lib reescrita em outra linguagem)
da biblioteca lib_hash_ans.
Outras linguagens (Python, Rust, C, C++, PHP, etc.) seguem o mesmo contrato e os
mesmos vetores de conformidade. Para entender o problema que a lib resolve, veja
docs/USAGE.md (guia de uso) e
docs/ARCHITECTURE.md (conceitos e visão geral).
- Status: alpha. 20/20 vetores sintéticos PASS (18 positivos + 2 negativos) em
conformance/vectors.json. - Licença: MIT.
- Engines: Node.js
>=20(testado em 20 LTS e 22 LTS). - Dependência runtime única:
@xmldom/xmldom(DOM puro, pure-JS, sem deps nativas).
Antes de começar: instalar o Node.js
Node.js é o ambiente que executa JavaScript fora do navegador. Ele já vem com o
npm, o gerenciador que baixa e instala bibliotecas (dependências).
- Baixe e instale pelo site oficial: https://nodejs.org/ (escolha a versão "LTS"; precisa da 20 ou mais nova).
- Confira a instalação:
node --version
npm --versionInstalação
Uma dependência é uma biblioteca de terceiros que o seu código usa; o npm
a baixa e instala. O comando abaixo adiciona esta lib ao seu projeto:
npm install tiss-hashA publicação no npm registry (o repositório oficial de pacotes Node.js) ainda não foi feita. Por enquanto, instale diretamente a partir do checkout do repositório (a pasta que você baixou com
git clone):npm install /caminho/para/lib_hash_ans/langs/node
Quickstart
ESM (recomendado)
import { hashTiss, hashTissFile } from 'tiss-hash';
import { readFileSync } from 'node:fs';
// A partir de bytes (sincrono)
const md5 = hashTiss(readFileSync('lote.xml'));
console.log(md5); // ex.: '3aa0c578c95cdb861a125f480a8a4de5'
// A partir de um caminho de arquivo (assincrono)
const md5b = await hashTissFile('lote.xml');CommonJS
const { hashTiss, hashTissFile } = require('tiss-hash');
const { readFileSync } = require('node:fs');
const md5 = hashTiss(readFileSync('lote.xml'));Tratamento de erro
import { hashTiss, InvalidTissXmlError } from 'tiss-hash';
try {
hashTiss(Buffer.from('<nao-eh-xml'));
} catch (err) {
if (err instanceof InvalidTissXmlError) {
console.error('XML rejeitado:', err.message);
} else {
throw err;
}
}API
| Símbolo | Tipo | Descrição |
| --- | --- | --- |
| hashTiss(xmlBytes) | (Uint8Array \| Buffer) => string | Hash MD5 (hex, 32 chars minúsculo) a partir dos bytes do XML. Sincrono. |
| hashTissFile(filePath) | (string \| URL) => Promise<string> | Atalho assíncrono: lê o arquivo e calcula o hash. |
| InvalidTissXmlError | class extends Error | Lançada quando o XML é mal-formado ou rejeitado pelo parser. |
| TISS_NAMESPACE | string | URI do namespace TISS: http://www.ans.gov.br/padroes/tiss/schemas. |
Algoritmo
Resumo do que hashTiss faz:
- Decodifica os bytes do XML respeitando a declaração
encoding="...":iso-8859-1(padrão TISS): mapeia byte para codepoint (latin1) e reescreve a declaração parautf-8antes de passar ao parser.utf-8: usa direto. Strippa BOM UTF-8 se presente.
- Parseia o XML com
@xmldom/xmldom(DOM W3C compliant, mantém comentários emchildNodes). - Localiza o
<ans:hash>(por namespace URI, não por prefixo) e marca-o para ser zerado. Documento sem<ans:hash>é válido; com múltiplos<ans:hash>é rejeitado (erro). - Caminha a árvore em ordem de documento (depth-first, pre-order).
Para cada nó-folha, isto é,
ElementouCommentcujos filhos NÃO contêmElement/Comment/ProcessingInstruction, concatenatextContentao buffer. O<ans:hash>contribui"". - Calcula
MD5sobre os bytes UTF-8 da string concatenada. - Devolve o
hexdigest()em minúsculo (32 caracteres).
Atenção: encoding do MD5 é UTF-8, não ISO-8859-1
O manual TISS (Componente Organizacional nov/2025, pág 53, item 146) diz "O encoding a ser utilizado será sempre o ISO-8859-1". Essa frase é ambígua e foi historicamente mal interpretada. Refere-se ao encoding do arquivo XML, não dos bytes que alimentam o MD5.
Na prática (validada contra goldens reais privados, fora do repo, além dos vetores sintéticos públicos), os valores extraídos do XML são re-encodados em UTF-8 antes do MD5. Implementações que aplicam MD5 sobre bytes ISO-8859-1 produzem hash diferente e errado.
Spec completa: docs/SPEC.md.
Catálogo das 15 decisões canônicas (CDATA, entidades, atributos,
comentários, etc.): conformance/AMBIGUITY_NOTES.md.
Decisão de parser: @xmldom/xmldom
Avaliadas três opções:
| Parser | Decisão | Motivo |
| --- | --- | --- |
| @xmldom/xmldom | escolhido | DOM puro, pure-JS, sem deps nativas. Semântica W3C DOM próxima do ElementTree/lxml do Python. Mantém nós Comment (nodeType 8) em childNodes por padrão, essencial pra reproduzir a ambiguidade #2 da referência (comentários XML ENTRAM no concat). |
| fast-xml-parser | descartado | Converte XML para objeto JS, perde noção de ordem e o conceito de "elemento-folha" sem reconstrução manual. |
| sax / ltx | descartado | Streaming/SAX exigem reconstruir manualmente a árvore. Sem ganho real (XMLs TISS < 10 MB). |
Conformidade
Esta lib passa os 20 vetores sintéticos (18 positivos + 2 negativos) em
conformance/vectors.json,
cobrindo:
- 18 positivos: mínimo, acentuação (UTF-8 vs ISO-8859-1), campos vazios,
CR/LF, múltiplas guias, entidades XML, entidades numéricas, CDATA,
comentários, atributos, namespaces variados, namespace default, sem
<ans:hash>(válido), whitespace puro, zeros à esquerda, símbolos ISO-8859-1, performance (~600 KB), BOM UTF-8. - 2 negativos (esperam erro):
syn_multi_hash.xml(múltiplos<ans:hash>) esyn_utf16.xml(UTF-16 fora de escopo).
A lista canônica vive em conformance/vectors.json.
Cada vetor é um par "arquivo de entrada -> hash esperado": positivo deve produzir um hash, negativo deve ser rejeitado (a lib precisa recusar o arquivo, em vez de inventar um hash).
Rodar os testes localmente, a partir desta pasta:
npm install # baixa as dependências
npm test # roda os 20 vetores de conformidadeSaída esperada: 20 vetores passando (incluindo os 2 negativos que devem lançar erro) + asserts de API auxiliares.
Limitações conhecidas
- Múltiplos
<ans:hash>no mesmo documento: rejeitado (lançaInvalidTissXmlError). Cenário patológico, não previsto pelo padrão TISS; falhar é mais seguro que adivinhar qual zerar. - Encodings suportados: ISO-8859-1 e UTF-8. UTF-16/UTF-32 são rejeitados (fora de escopo).
- Bytes inválidos no encoding declarado: comportamento não fixado pela suite. O parser tipicamente substitui ou rejeita; a lib não tenta recuperar.
Dependências e licenças
Dependência de runtime única (MD5 vem do node:crypto, sem deps nativas):
| Dependência | Licença | Uso |
| --- | --- | --- |
| @xmldom/xmldom | MIT | parser DOM (pure-JS) |
Atribuição consolidada de todos os ports em
THIRD_PARTY_LICENSES.md na raiz do repo.
Licença
MIT Copyright (c) 2026 Petrus Silva Costa
Ver também
docs/USAGE.md: guia de uso, receitas e perguntas frequentes (comece por aqui se você quer só usar a lib).docs/ARCHITECTURE.md: conceitos e visão geral.docs/SPEC.md: especificação canônica do algoritmo.docs/PORTING_GUIDE.md: guia para portar para outras linguagens.conformance/reference.py: implementação de referência (o "oráculo" que define a resposta certa).
