Concepts
A CableProbe session runs three phases in order:
| Phase | What happens |
|---|---|
baseline | Observe the host before the unknown cable is connected. |
test | You connect / power the unknown cable; CableProbe keeps observing. |
post_test | You disconnect the cable; CableProbe observes the return to baseline. |
CableProbe compares the three phases and writes a structured JSON report describing everything that appeared, disappeared or changed in correlation with the cable, then runs a set of YAML-configurable detection rules over those differences to produce prioritised findings.
Probes (observation only)
All 18 probes are enabled by default. Each one only
reads host state. A probe whose interface the host does not
expose (no Type-C class, no /sys/bus/pci, …) reports itself
unavailable in cableprobe check and is skipped — that is not
an error.
| Group | Probe | Observes |
|---|---|---|
| USB devices & topology | usb |
USB device inventory: vendor/model, interface classes, HID, hubs. |
usb_descriptors |
Per-interface USB descriptors from sysfs: class, subclass, driver, endpoints. | |
usb_topology |
The USB hub and port tree: hub count, device count, depth, per-hub inventory. | |
| USB-C & Power Delivery | usbc_pd |
USB-C / Power Delivery port and partner state: data and power roles, alternate modes. |
| Storage | block |
Block devices (disks, partitions) and their transport. |
mounts |
Filesystem mounts backed by a device or under removable-media paths. | |
| Network | network |
Network interfaces: driver, transport, USB-ness, addresses. |
routing |
The default route and DNS resolvers — catches gateway or resolver hijack. | |
listeners |
TCP sockets in LISTEN state, with the owning process where available. | |
| Peripherals | input |
Input / HID devices: keyboards, mice, tablets. |
serial |
Serial and modem (TTY) devices, including USB serial (CDC-ACM, FTDI, cp210x). | |
audio |
Audio (sound-card) devices, including the USB audio class. | |
video |
video4linux camera / capture devices, including UVC webcams. | |
| System & kernel | udev_monitor |
Live udev add/remove/change events across every subsystem, as they happen. |
pci |
PCI and Thunderbolt devices — the USB4 / Thunderbolt PCIe-tunnel and DMA surface. | |
kernel_modules |
Loaded kernel modules — catches gadget drivers loaded on connect. | |
process |
Processes started after the session began. | |
kernel_log |
USB-relevant kernel and journal lines emitted during the session. |
Commands
| Command | Purpose |
|---|---|
cableprobe check | Verify the host has what the enabled probes need. |
cableprobe run | Run a full baseline / test / post-test session and write a report. |
cableprobe probes | List the available probes. |
cableprobe rules [FILE] | Show the built-in detection rules, or validate a rules file. |
cableprobe report FILE | Print a saved .cableprobe.json report (--format summary|json). |
cableprobe --version | Show the version and exit. |
run options
| Option | Meaning |
|---|---|
--baseline N | Baseline phase duration in seconds. |
--test N | Test phase duration in seconds. |
--post-test N | Post-test phase duration in seconds. |
--interval F | Sampling interval in seconds. |
--config, -c FILE | YAML config file. |
--rules, -r FILE | YAML rules file (default: built-in). |
--output-dir, -o DIR | Where to write the report. |
--name, -n NAME | Human name for this session. |
--auto | Do not wait for operator input; advance phases on a timer. |
--fail-on-findings | Exit non-zero when medium+ findings are present. |
--verbose, -v | -v info, -vv debug. |
Usage
# Check the host is ready
cableprobe check
# Run a session (you will be prompted before each phase)
sudo cableprobe run --name "suspect-cable-01" \
--baseline 30 --test 90 --post-test 30
# Unattended / rig mode: advance phases on a timer instead of prompts
sudo cableprobe run --auto --baseline 20 --test 60 --post-test 20
# Inspect a saved report
cableprobe report cableprobe-sessions/2026*.cableprobe.json
cableprobe report <file> --format json | jq .
# See / customise detection rules
cableprobe rules
cableprobe rules my-rules.yaml
Running with sudo is recommended: the kernel-log and
udev-attribute probes see more detail with privileges.
Exit codes
cableprobe run --fail-on-findings exits 10 /
20 / 30 for the highest finding severity
(medium / high / critical),
otherwise 0. Without the flag, run always exits
0 on a completed session.
Configuration
Optional YAML config, passed with --config cableprobe.yaml.
probes.enabled defaults to the full list below; trim it to
run fewer.
session:
baseline_seconds: 30
test_seconds: 90
post_test_seconds: 30
sample_interval_seconds: 2.0
probes:
enabled: [udev_monitor, usb, usb_descriptors, usb_topology, usbc_pd, block,
mounts, network, routing, listeners, input, serial, audio, video, pci,
kernel_modules, process, kernel_log]
kernel_log_backend: auto # auto | journalctl | dmesg
kernel_log_keywords: [] # extra case-insensitive substrings to keep
rules_file: null # null => packaged default rules
output_dir: ./cableprobe-sessions
Detection rules
Rules live in YAML (see cableprobe/data/default_rules.yaml).
Each rule matches a phase delta and raises a finding:
- id: hid-keyboard-appeared-on-connect
title: HID keyboard appeared while the unknown cable was connected
severity: high
rationale: >
A keyboard-class HID device that enumerates only when the cable is
connected is the classic BadUSB signature.
match:
change: appeared # appeared | disappeared | modified
first_seen_phase: test # baseline | test | post_test
kind: [input_device, hid_device]
attributes:
any:
- { key: ID_INPUT_KEYBOARD, equals: "1" }
Match keys: change, kind,
first_seen_phase, reverted_after_disconnect,
transient, label_regex,
event_action, and attributes.all /
attributes.any (conditions: equals,
not_equals, exists, contains,
regex).
Severities: info, low,
medium, high, critical.
Report structure
{
"metadata": { "session_name", "host", "config", "probes_used", "probe_warnings", ... },
"phases": { "baseline": {...}, "test": {...}, "post_test": {...} },
"deltas": [ { "change", "kind", "identity", "label",
"first_seen_phase", "present_in",
"reverted_after_disconnect", "transient",
"attributes", "attribute_changes", "related_events" } ],
"findings": [ { "rule_id", "title", "severity", "rationale", "evidence" } ],
"summary": { "delta_count", "cable_correlated_change_count",
"findings_by_severity", "highest_severity", ... }
}
Third-party dependencies
CableProbe does not copy or vendor third-party source code. It depends on a small set of Python libraries and, at runtime, invokes standard Linux command-line tools as separate, independently installed programs.
| Package | Licence | Notes |
|---|---|---|
typer | MIT | CLI framework |
pydantic | MIT | data models |
psutil | BSD-3-Clause | process / network inventory |
PyYAML | MIT | config and rules parsing |
rich | MIT | console rendering |
pyudev | LGPL-2.1-or-later | Linux-only; imported as a library, not vendored or modified |
pytest, pytest-asyncio | MIT, Apache-2.0 | dev only |
Note on pyudev: it is a normal, separately
installed dependency that CableProbe imports; it is not vendored or
modified. If you redistribute CableProbe as a bundled binary/image, keep
pyudev as a replaceable component and include its licence
text. The Linux CLI tools CableProbe shells out to
(lsusb/usbutils, lsblk/dmesg/util-linux,
journalctl/systemd) are invoked as independent programs and do
not affect CableProbe's MIT licensing.
Scope & non-goals
- CLI only — no web dashboard.
- The
usbc_pdprobe reads the kernel's USB-C / Power Delivery port state (roles, alternate modes); CableProbe does not do electrical measurement of the cable itself (voltage, current, e-marker interrogation). - No offensive capability of any kind, by design.
See Contributing for how to propose changes, and Security & trust for how to report vulnerabilities privately.