ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
PENTEST

Pentesting / Adversarial Suite

A separately-runnable harness that attacks the server the way a hostile client would, and asserts the guarantees that keep a deterministic device safe. It is not part of the per-commit unit-test run (python test/harness.py run): it is heavier, randomized, and meant to be run on demand or on a schedule.

‍## ⚖️ Authorized use only

The live driver (penetration_testing/pc_pentest.py) and the on-device techniques below send real attack traffic. Use them only against a device you own or have explicit, written permission to test. Unauthorized access to or interference with computer systems is a crime in most jurisdictions (US CFAA, UK Computer Misuse Act, and equivalents). You, the operator, are solely responsible for staying within the law and the scope of your authorization. This tooling is provided as is, with no warranty and no liability. We build defensive tooling - use it for good. The live driver refuses to run until you pass --authorized.

There are three layers:

  1. Host fuzz (env:native_pentest) - throws malformed, oversized, partial, binary, and deterministically-random input at the untrusted-input parsers and checks the safety invariants. Fast, reproducible, CI-friendly.
  2. Live adversarial driver (penetration_testing/pc_pentest.py) - a feature-aware Python tool that drives a flashed board over the network, auto-detects the build's features, runs the applicable attacks, and checks liveness + heap-drift after each one. See penetration_testing/README.md.
  3. Manual on-device stress - ad-hoc curl/nc techniques for one-off probing.

The invariants under test

Every adversarial input must preserve all three. A violation is a finding.

Invariant What it means How it is checked
Fixed footprint No fixed buffer index ever exceeds its compile-time bound (no overflow). After every input: path_idx <= MAX_PATH_LEN, header_count <= MAX_HEADERS, body_len <= BODY_BUF_SIZE, response length <= cap, etc.
Fail-closed Bad input lands in a defined error state, never an undefined one. Parser state is always within the ParseState enum; bad requests reach PARSE_ERROR (400) / PARSE_ENTITY_TOO_LARGE (413) / PARSE_URI_TOO_LONG (414).
Liveness The parser always terminates - no hang, no over-read. The harness feeds bounded streams and returns; a hang/crash/sanitizer trip fails the run.

These follow from the library's core promise: no heap after begin(), all buffers statically sized, so the only way adversarial input can hurt the device is an overflow or an undefined state - exactly what this suite hunts.