OT posture rules
Cyprobe evaluates discovered assets against posture rules written in YAML. The rule schema is PeriDex -- the same format used by cyscan for SAST rules and the Cybrium CSPM engine for cloud posture. One schema across code, cloud, and OT.
Running a posture check
sudo cyprobe passive --interface eth0 --duration 300 --format json > assets.json
cyprobe rules check --input assets.json
Or inline after an active probe:
sudo cyprobe active --targets 192.168.1.0/24 --active-confirm --format json \
| cyprobe rules check --input -
Rule format
id: CYPROBE-OT-MODBUS-NO-AUTH
title: "Modbus TCP endpoint has no authentication"
severity: high
protocol: modbus
cwe: [CWE-306]
iec62443: [SR-1.1]
message: |
Modbus TCP has no built-in authentication. Any host on the network can
read/write coils and registers. Segment the device behind a firewall or
deploy a Modbus-aware gateway with access controls.
match:
protocol: modbus
field: auth_required
operator: eq
value: false
Required fields
| Field | Type | Notes |
|---|---|---|
id | string | Stable identifier. Convention: CYPROBE-OT-<PROTOCOL>-<NAME> |
title | string | One-line summary |
severity | critical / high / medium / low / info | |
protocol | string | Protocol this rule applies to, or any |
message | multi-line string | Description + remediation guidance |
match | object | Condition to evaluate against asset data |
Optional fields
| Field | Type | Notes |
|---|---|---|
cwe | list | CWE identifiers |
iec62443 | list | IEC 62443 security requirement references |
nist_sp | list | NIST SP 800-82 control references |
Match operators
| Operator | Description | Example |
|---|---|---|
eq | Equals | auth_required eq false |
ne | Not equals | tls_version ne 1.2 |
lt | Less than | firmware_age_days lt 365 |
gt | Greater than | open_port_count gt 5 |
contains | String/list contains | security_mode contains None |
regex | Regex match | firmware_version regex ^1\. |
Seed rules
Cyprobe ships with 10 seed rules covering the most common OT posture gaps:
| ID | Severity | Protocol | What it checks |
|---|---|---|---|
CYPROBE-OT-MODBUS-NO-AUTH | high | modbus | Modbus endpoint accepts unauthenticated requests |
CYPROBE-OT-OPCUA-SECURITY-NONE | critical | opcua | OPC UA endpoint allows SecurityMode None |
CYPROBE-OT-OPCUA-NO-CERT | high | opcua | OPC UA endpoint has no server certificate |
CYPROBE-OT-MQTT-NO-AUTH | high | mqtt | MQTT broker accepts anonymous connections |
CYPROBE-OT-MQTT-NO-TLS | medium | mqtt | MQTT traffic is unencrypted (port 1883) |
CYPROBE-OT-S7-DEFAULT-RACK | medium | s7comm | S7 PLC on default rack 0 / slot 2 (factory config) |
CYPROBE-OT-DNP3-NO-AUTH | high | dnp3 | DNP3 Secure Authentication not enabled |
CYPROBE-OT-BACNET-WRITE-OPEN | high | bacnet | BACnet device allows unauthenticated writes |
CYPROBE-OT-FW-OUTDATED | medium | any | Firmware older than 365 days |
CYPROBE-OT-FLAT-NETWORK | high | any | IT and OT assets share the same L2 broadcast domain |
Writing custom rules
- Create a
.ymlfile in the rules directory. - Follow the schema above.
- Validate:
cyprobe rules validate - Test against a known asset inventory:
cyprobe rules check --input assets.json --rules ./my-rules
Example: flag any device with firmware older than 180 days
id: ACME-OT-FW-STALE
title: "Firmware not updated in 180 days"
severity: medium
protocol: any
message: |
Device firmware has not been updated in over 180 days. Review the vendor's
advisory feed and schedule a maintenance window for patching.
match:
protocol: any
field: firmware_age_days
operator: gt
value: 180
ID conventions
The bundled rules use the CYPROBE-OT-* prefix. Use a different prefix for your own rules (e.g. ACME-OT-*) to avoid collisions on upgrade.
Next step
- Platform integration -- upload posture findings to Cybrium