Sniff and decode BLE GATT traffic, fingerprint device profiles, and assert on insecure pairing/characteristics in CI against a capture.
IoT / OT / Embedded — firmware, buses, and device security.
pip install cognis-blescope
blescope scan capture.json # → prioritized findings in seconds (passive, offline)Real, reproducible output from the tool — runs offline:
$ blescope-emit --version
blescope 0.6.0$ blescope-emit --help
usage: blescope [-h] [--version] {scan,scan-live} ...
Decode a BLE GATT capture, fingerprint the device profile, and assert on insecure pairing.
positional arguments:
{scan,scan-live}
scan analyze a BLE GATT capture file (use '-' for stdin)
scan-live ACTIVE: pull live GATT from authorized in-scope BLE
devices
options:
-h, --help show this help message and exit
--version show program's version number and exit
examples:
blescope scan capture.json
blescope scan capture.json --format json | jq .findings
cat capture.json | blescope scan -Blocks above are real
blescopeoutput — reproduce them from a clone.
Sample result format (illustrative values — run on your own data for real findings):
{
"timestamp": "2023-02-15T14:30:00Z",
"findings": [
{
"id": "1234567890abcdef",
"title": "Suspicious Network Traffic",
"description": "Network traffic detected from unknown IP address",
"labels": ["network", "suspicious"],
"observed_data": {
"start_time": "2023-02-15T14:20:00Z",
"end_time": "2023-02-15T14:25:00Z"
},
"recommended_actions": [
{"action": "Block IP address", "confidence": 0.8}
]
}
]
}
blescope decodes a BLE GATT capture, fingerprints the device profile, and asserts on insecure pairing/access.
- Install (Python 3.10+):
pip install -e . # or: pipx install blescope
- Scan a capture (human-readable report):
blescope scan demos/01-basic/frontdoor_lock.json
- Filter to actionable findings at/above a severity:
blescope scan capture.json --min-severity medium
- Read the output as JSON for piping / dashboards, or SARIF for code-scanning:
blescope scan capture.json --format json | jq '.findings' blescope scan capture.json --format sarif > blescope.sarif # GitHub code-scanning cat capture.json | blescope scan - # stdin
- Gate CI on insecure devices — exit
1when any actionable finding is reported,0when clean,2on usage error:- run: pip install -e . && blescope scan capture.json # non-zero fails the job
blescope has two modes. Passive is the default and the safe one.
Analyzes a capture you provide (a JSON or text GATT capture, a saved sniff, an SBOM-style device inventory). It never opens a radio or contacts anything. This is what runs in CI.
blescope scan capture.json # offline analysis of provided input
cat capture.json | blescope scan - # stdin
⚠️ AUTHORIZED USE ONLY. Active mode pulls live GATT data from a BLE device/adapter. Only probe devices you own or have explicit written permission to test. Active scanning of third-party devices may be illegal.
Active mode is engineered as a hard opt-in and is OFF by default:
--authorizedis mandatory — without it the command refuses (exit 2).--target-allowlistis mandatory and non-empty — a comma list of BLE addresses, or a path to a file of addresses, that are in scope. Devices not in the allowlist are skipped, never probed.--raterate-limits every probe (default1.0/s; a token bucket paces it).- A loud "AUTHORIZED USE ONLY" banner prints before any active operation.
- With no real BLE backend wired in, blescope refuses rather than fabricate
data. Use
--demoto exercise the active pipeline offline against bundled fixtures (no radio, no network).
# Refuses — active is off by default:
blescope scan-live --target-allowlist AA:BB:CC:DD:EE:01 # exit 2
# Authorized, scoped, rate-limited demonstration against bundled fixtures:
blescope scan-live --authorized \
--target-allowlist AA:BB:CC:DD:EE:01 \
--rate 1 --demo --format jsonA real adapter backend (e.g. a bleak-based Scanner) is injected by the
operator; the bundled default never invents device data, and the test suite uses
only a MockScanner/localhost fixtures — never a real external host.
Ten worked scenarios live in demos/ — each is a real-format capture plus a SCENARIO.md (where the data came from, the exact run command, and how to act). Every demo is exercised by the test suite, so they always fire.
| Demo | Profile | Headline finding |
|---|---|---|
01-basic |
smart_lock | Just Works + plaintext unlock write (critical) |
02-clean |
fitness_tracker | clean baseline — zero findings, exit 0 |
03-mixed |
smart_lock | well-paired plug, leaky control characteristic |
04-debug-keys |
smart_lock | factory debug keys mask strong pairing |
05-fitness-legacy |
fitness_tracker | LE Legacy Pairing (no Secure Connections) |
06-hid-keyboard |
hid_peripheral | NoInputNoOutput forces Just Works on a keyboard |
07-no-smp-sensor |
environmental_sensor | no SMP exchange — unencrypted link |
08-beacon-open |
beacon | proximity beacon left connectable + open |
09-secure-lock |
smart_lock | a lock done right — clean reference |
10-text-relay |
smart_lock | tolerant text-capture format, full insecure stack |
python -m blescope scan demos/04-debug-keys/smartbulb_debugkey.json
python -m blescope scan demos/09-secure-lock/secure_deadbolt.json # exits 0- Why blescope? · Features · Quick start · Example · Edge / air-gap · Architecture · AI stack · How it compares · Integrations · Install anywhere · Related · Contributing
Smart-lock and wearable teardown culture — 'this $200 lock pairs Just-Works and exposes a writable unlock characteristic' is endlessly shareable; reproducible from a saved capture.
blescope is single-purpose, scriptable, and self-hostable: point it at a target, get prioritized results in the format your workflow already speaks (table · JSON · SARIF), gate CI on it, and let agents drive it over MCP.
- ✅ Decode GATT service/characteristic UUIDs (16-bit + Bluetooth-base 128-bit)
- ✅ Load captures from JSON or a tolerant
key: valuetext form (and stdin) - ✅ Fingerprint device profile (smart_lock · fitness_tracker · beacon · hid_peripheral · environmental_sensor)
- ✅ Audit SMP pairing (Just Works, LE Legacy, weak key, NoInputNoOutput, debug keys, missing SMP)
- ✅ Flag plaintext / unauthenticated writes to control characteristics
- ✅
table·json·sarifoutput (SARIF 2.1.0 for GitHub code-scanning) - ✅ Privacy & bonding checks (static/public address tracking, unauthenticated bonded LTK)
- ✅ CI-ready exit codes with a
--min-severitygate - ✅ Passive by default (offline); optional authorization-gated active live pull (
scan-live, OFF by default, scope-enforced, rate-limited) - ✅ Runs on Linux/macOS/Windows · Docker · devcontainer
- ✅ Ports in Python, JavaScript, TypeScript, Go, Rust, Perl, Ruby, and Shell+awk (
ports/) — passive core, CI-built, finding-ID parity verified against the reference
pip install cognis-blescope
blescope --version
blescope scan capture.json # passive: analyze a capture
blescope scan capture.json --format json # machine-readable
blescope scan capture.json --min-severity high # CI gate (non-zero exit)$ blescope scan demos/01-basic/frontdoor_lock.json
============================================================
BLESCOPE report (blescope 0.6.0)
============================================================
Device : FrontDoorLock [AA:BB:CC:DD:EE:FF]
Profile : smart_lock (confidence 100%)
Findings (6):
[CRIT] SMP-JUSTWORKS: Just Works pairing (no MITM protection)
[CRIT] ATT-PLAINTEXT-CTRL: Plaintext write to control characteristic 2a56
[HIGH] SMP-LEGACY: LE Legacy Pairing (no Secure Connections)
[HIGH] SMP-WEAKKEY: Short encryption key (7 bytes)
[HIGH] GATT-UNAUTH-WRITE: Unauthenticated writable control characteristic 2a56
[MED ] SMP-IOCAP: NoInputNoOutput I/O capability forces Just Works
------------------------------------------------------------
VERDICT: INSECURE (worst severity: critical)
------------------------------------------------------------
$ blescope scan demos/01-basic/frontdoor_lock.json --format json | jq '.profile, .insecure, [.findings[].id]'
"smart_lock"
true
[
"SMP-JUSTWORKS",
"ATT-PLAINTEXT-CTRL",
"SMP-LEGACY",
"SMP-WEAKKEY",
"GATT-UNAUTH-WRITE",
"SMP-IOCAP",
"SMP-WEAKBOND"
]--format sarif emits a SARIF 2.1.0 log: BLE severities map to error/warning/note,
each rule carries a CVSS-like security-severity, and the result is uploadable straight
to GitHub code-scanning.
$ blescope scan demos/01-basic/frontdoor_lock.json --format sarif | \
jq '.runs[0].results[0] | {ruleId, level, security: .properties["ble-severity"]}'
{
"ruleId": "SMP-JUSTWORKS",
"level": "error",
"security": "critical"
}# .github/workflows/ble-audit.yml — gate a PR on BLE captures
- run: pip install cognis-blescope
- run: blescope scan captures/device.json --format sarif > blescope.sarif
- uses: github/codeql-action/upload-sarif@v3
with: { sarif_file: blescope.sarif }blescope is built to run where there is no internet:
- Zero network, zero telemetry. The passive engine reads a local capture and nothing else — no feed fetch, no phone-home, no external lookups. UUID names, profile fingerprints, and pairing rules are all bundled in the code.
- Stdlib-only Python core. No third-party runtime dependency to vendor.
- Most-portable ports for locked-down boxes. The Perl port needs only the
core
JSON::PP(ships with Perl 5); the Shell+awk port needs only a POSIX shell andawk. Both are present on essentially every Unix host out of the box, so you can drop a single file onto an air-gapped analyst workstation and audit a capture with nothing to install. - Single-file deploy. Copy
ports/perl/blescope.plorports/shell/blescope.sh(+blescope.awk) and run — no package manager, no virtualenv.
blescope does not consume any external vulnerability/threat feed: its findings come entirely from the BLE/SMP specification rules and the Bluetooth SIG assigned-numbers tables compiled into the tool, so results are identical online or offline.
flowchart LR
IN[capture / scan] --> P[blescope<br/>parse + map]
P --> OUT[report]
blescope is interoperable with every popular way of using AI:
- MCP server —
blescope mcp(Claude Desktop, Cursor, Cognis.Studio, uncensored-fleet) - OpenAI-compatible / JSON — pipe
blescope scan . --format jsoninto any agent or LLM - LangChain · CrewAI · AutoGen · LlamaIndex — wrap the CLI/JSON as a tool in one line
- CI / scripts — exit codes + SARIF for non-AI pipelines
| Cognis blescope | Sniffle + gattacker | |
|---|---|---|
| Self-hostable, no account | ✅ | varies |
| Single command, zero config | ✅ | |
| JSON + SARIF for CI | ✅ | varies |
| MCP-native (AI agents) | ✅ | ❌ |
| Polyglot ports (JS/TS/Go/Rust/Perl/Ruby/Shell) | ✅ | ❌ |
| Air-gap single-file deploy | ✅ | |
| Open license | ✅ COCL | varies |
Built in the spirit of Sniffle + gattacker, re-framed the Cognis way. Missing a credit? Open a PR.
Pipes into your stack: SARIF for code-scanning, JSON for anything, an MCP server (blescope mcp) for AI agents, and a webhook forwarder for SIEM/Slack/Jira. See docs/INTEGRATIONS.md.
pip install "git+https://github.com/cognis-digital/blescope.git" # pip (works today)
pipx install "git+https://github.com/cognis-digital/blescope.git" # isolated CLI
uv tool install "git+https://github.com/cognis-digital/blescope.git" # uv
pip install cognis-blescope # PyPI (when published)
docker run --rm ghcr.io/cognis-digital/blescope:latest --help # Docker
brew install cognis-digital/tap/blescope # Homebrew tap
curl -fsSL https://raw.githubusercontent.com/cognis-digital/blescope/main/install.sh | sh| Linux | macOS | Windows | Docker | Cloud |
|---|---|---|---|---|
scripts/setup-linux.sh |
scripts/setup-macos.sh |
scripts/setup-windows.ps1 |
docker run ghcr.io/cognis-digital/blescope |
DEPLOY.md (AWS/Azure/GCP/k8s) |
fwxray— Diff two firmware images and surface exactly what changed: new binaries, flipped config flags, added certs, and shifted entropy regions.canzap— Replay, fuzz, and assert on CAN bus traffic from a .pcap or SocketCAN interface with a tiny YAML DSL.sbomb— Generate a CycloneDX SBOM directly from an unpacked firmware root filesystem and flag components with known CVEs and EOL kernels.mqttspy— Passively map an MQTT broker: enumerate topics, detect unauthenticated writes, spot PII/secrets in payloads, and emit a risk report.uefiscan— Audit UEFI firmware dumps for missing Secure Boot keys, unsigned modules, S3 boot-script vulns, and known SMM threats.modpot— Spin up a high-interaction Modbus/DNP3 ICS honeypot that logs attacker register reads/writes as structured JSON.
Explore the suite → 🗂️ all 170+ tools · ⭐ awesome-cognis · 🔗 cognis-sources · 🤖 uncensored-fleet · 🧠 engram
PRs, new rules, and demo scenarios are welcome under the collaboration-pull model — see CONTRIBUTING.md and SECURITY.md.
blescope is a defensive, authorized-use tool for engineers, researchers, and
security teams auditing devices they own or are explicitly permitted to test.
- Passive by default.
blescope scanonly reads a capture you already have. It never opens a radio, never contacts a network, and is what runs in CI. - Active mode is a hard opt-in.
blescope scan-liveis OFF by default and refuses to run without--authorizedand a non-empty--target-allowlist. Devices not on the allowlist are skipped, never probed; every probe is rate-limited; a loud "AUTHORIZED USE ONLY" banner prints first. - Read-only probes, no exploitation. Active mode enumerates GATT and inspects pairing posture — it sends no exploit payloads, no auth-bypass, no actuation writes. It reports risk; it does not attack.
- No fabricated data. With no real BLE backend wired in, blescope refuses
rather than invent device data. Tests use only local fixtures /
MockScanner. - You are responsible for ensuring you have permission. Active scanning of third-party devices may be illegal in your jurisdiction.
blescope composes with the 300+ tool Cognis suite — JSON in/out and a shared
OpenAI-compatible /v1 backbone. See INTEROP.md for the
suite map, composition patterns, and reference stacks.
Source-available under the Cognis Open Collaboration License (COCL) v1.0 — free for personal, internal-evaluation, research, and educational use; commercial / production use requires a license ([email protected]). See LICENSE.