CableProbe
CableProbe is an open-source defensive USB cable analysis tool for Linux (developed and tested on Raspberry Pi OS / Debian, and reasonably portable to other Debian/Ubuntu systems).
- Home page: https://www.cableprobe.com
- Source & downloads: https://github.com/rosscooney/CableProbe (releases)
- Package:
cableprobeon PyPI
CableProbe watches a sacrificial Linux host while you connect an unknown USB-C-to-USB-C cable, then compares before, during and after to surface hidden HID devices, rogue network gadgets, mass storage, serial channels, transient enumeration and kernel errors.
CableProbe only observes, records and reports. It does not inject payloads, exploit anything, capture credentials, establish persistence or provide remote access. It is a monitoring tool for a sacrificial test host.
⚠️ CableProbe cannot prove that a cable is safe or uncompromised. A clean report means CableProbe did not observe anything notable during that session, not that the cable is benign. Use it as one input to your own judgement.
⚠️ Run this on a dedicated, disposable Raspberry Pi that holds no sensitive data and is isolated from networks you care about. Treat any cable under test as hostile hardware.
How it works
A session has three phases:
| Phase | What happens |
|---|---|
baseline |
Observe the Pi 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 then compares the three phases and writes a structured JSON report describing everything that appeared, disappeared or changed in correlation with the cable, and runs a set of YAML-configurable detection rules over those differences to produce prioritised findings.
Probes (observation only)
| Probe | Observes |
|---|---|
udev_monitor |
Live udev add/remove/change events across all subsystems. |
usb |
USB device inventory (vendor/model, interface classes, HID, hub). |
usb_descriptors |
Per-interface USB descriptors from sysfs (class/subclass/driver/endpoints). |
usb_topology |
USB hub/port tree — hub count, device count, depth, per-hub inventory. |
usbc_pd |
USB-C / Power Delivery port + partner state: data/power roles, alt modes. |
block |
Block devices and their transport (lsblk). |
mounts |
Filesystem mounts backed by a device or under removable-media paths. |
network |
Network interfaces, drivers, USB-ness, addresses. |
routing |
Default route and DNS resolvers (gateway / resolver hijack). |
listeners |
TCP sockets in LISTEN state (with owning process when ss is present). |
input |
Input / HID devices (keyboards, mice, tablets). |
serial |
Serial / modem (TTY) devices, including USB serial (CDC-ACM, FTDI, cp210x). |
audio |
Audio (sound-card) devices, including USB audio class. |
video |
video4linux camera / capture devices, including UVC. |
pci |
PCI and Thunderbolt devices (USB4/TBT PCIe-tunnel / 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 / journal lines emitted during the session. |
Probes that need hardware or kernel interfaces the host does not expose (no
Type-C class, no /sys/bus/pci, …) report themselves unavailable in
cableprobe check and are skipped — that is not an error.
Install
Requires Python 3.11+. See DISTRIBUTING.md for the full picture; the short version:
# 1. pip / pipx on an existing Raspberry Pi OS / Debian host
pipx install cableprobe
sudo apt install usbutils util-linux # CLI tools CableProbe shells out to
# 2. one command on a Pi (isolated venv under /opt/cableprobe)
sudo ./scripts/install.sh # sudo scripts/uninstall.sh to remove
# 3. from a checkout, for development
git clone https://github.com/rosscooney/CableProbe cableprobe && cd cableprobe
python3 -m venv .venv && . .venv/bin/activate
pip install -e ".[dev]"
For a disposable Raspberry Pi image, build your own from stock Raspberry Pi
OS using the pi-gen custom stage in packaging/pi-gen/ —
CableProbe ships the recipe, not a prebuilt image.
pyudev needs libudev (present on Raspberry Pi OS / Debian). On non-Linux
hosts CableProbe still installs and its --help / check / report commands
work, but the live probes are unavailable.
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 (--config cableprobe.yaml):
session:
baseline_seconds: 30
test_seconds: 90
post_test_seconds: 30
sample_interval_seconds: 2.0
probes:
# default: all probes; list a subset to narrow the session
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
capture_process_cmdline: true # false => store only the executable name
rules_file: null # null => packaged default rules
output_dir: ./cableprobe-sessions
Reports are written with mode 0600 (they can contain host details, MAC
addresses and process command lines). All device-supplied text (USB descriptor
strings, device names, kernel log lines) is stripped of control characters and
length-bounded before it is stored or shown, so a hostile cable cannot inject
terminal escape sequences via the report or the console summary.
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).
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", ... }
}
Development
pip install -e ".[dev]"
pytest
The analysis, rules and report layers are pure and fully unit-tested without
hardware. Each probe keeps a pure parser (of lsusb / lsblk / /proc
output) or sysfs-tree scanner that is tested against captured samples or a
fake /sys tree — see tests/test_probes_parsing.py and
tests/test_probes_new.py.
The source lives at https://github.com/rosscooney/CableProbe. See CONTRIBUTING.md for how to contribute, SECURITY.md for how to report vulnerabilities privately, and RELEASING.md for how maintainers cut a release to PyPI.
Scope / non-goals for v0.1
- CLI only — no web dashboard.
- No cable electrical / PD (power-delivery) analysis yet.
- No offensive capability of any kind, by design.
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. See
NOTICE.md and the "Third-party licensing" note below in the repo
history / docs.
Direct Python dependencies and their licences:
| 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 — see note |
| pytest, pytest-asyncio (dev only) | MIT, Apache-2.0 | test suite |
Note on pyudev: pyudev is LGPL-2.1-or-later. It is a normal, separately
installed Python dependency that CableProbe imports; it is not vendored or
modified. Distributing an MIT-licensed project that depends on an unmodified
LGPL library is standard practice, provided pyudev remains replaceable and its
source stays available (it is, from PyPI). 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 — GPL-2.0; journalctl/systemd — LGPL-2.1) are
invoked as independent programs and do not affect CableProbe's MIT licensing.
Licence
CableProbe is open-source software created by Stable State Consulting Ltd and released under the MIT License.
Copyright © 2026 Stable State Consulting Ltd.
See LICENSE for details.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file cableprobe-0.1.2.tar.gz.
File metadata
- Download URL: cableprobe-0.1.2.tar.gz
- Upload date:
- Size: 59.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4966cea01e193e31cfb5a6cf53f6951a9092022d669240969f17022b1130fafa
|
|
| MD5 |
ffd34c5707ffd93b38f228e67e80b936
|
|
| BLAKE2b-256 |
a50cc7de75c2aed0c2441af6bcbda63c917e3b6eabdb6bc556d712c3abe08c32
|
Provenance
The following attestation bundles were made for cableprobe-0.1.2.tar.gz:
Publisher:
python-publish.yml on rosscooney/CableProbe
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cableprobe-0.1.2.tar.gz -
Subject digest:
4966cea01e193e31cfb5a6cf53f6951a9092022d669240969f17022b1130fafa - Sigstore transparency entry: 2752710049
- Sigstore integration time:
-
Permalink:
rosscooney/CableProbe@c6f30ff8b27dc5980455f88e215ecf5a4098d147 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/rosscooney
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@c6f30ff8b27dc5980455f88e215ecf5a4098d147 -
Trigger Event:
release
-
Statement type:
File details
Details for the file cableprobe-0.1.2-py3-none-any.whl.
File metadata
- Download URL: cableprobe-0.1.2-py3-none-any.whl
- Upload date:
- Size: 64.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c4c74a999e16e57182660485a09b36592f1045793a286133060dde671f008486
|
|
| MD5 |
d233ca051ce84926c7167993f139b2b7
|
|
| BLAKE2b-256 |
5ec5807010a59c8bc1feb62865703ea4e232d3b22bb9a4672d584f353e090c87
|
Provenance
The following attestation bundles were made for cableprobe-0.1.2-py3-none-any.whl:
Publisher:
python-publish.yml on rosscooney/CableProbe
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cableprobe-0.1.2-py3-none-any.whl -
Subject digest:
c4c74a999e16e57182660485a09b36592f1045793a286133060dde671f008486 - Sigstore transparency entry: 2752710075
- Sigstore integration time:
-
Permalink:
rosscooney/CableProbe@c6f30ff8b27dc5980455f88e215ecf5a4098d147 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/rosscooney
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@c6f30ff8b27dc5980455f88e215ecf5a4098d147 -
Trigger Event:
release
-
Statement type: