Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.1.2] — 2026-06-30
## [0.1.2] - 2026-06-30

### Clarified

- **`family_id` scope boundary** (`python/bundle_spec/taxonomy.py` docstring + `README.md`): documented that `family_id` is the **validator / node-software** vulnerability-class vocabulary, and that on-chain contract / DeFi-economic findings (AMM value-extraction, oracle/TWAP manipulation, liquidation, governance capture, bridge-contract message abuse, ERC-4626/reentrancy logic) are **out of scope** — a separate DeFi-economic namespace, never `family_id` values, cross-mapped in the VII `ledger/family_map.csv`. Records the canonical-vocabulary ratification (the 11-value `FamilyId` enum, unchanged). **No enum/wire change** — `BUNDLE_VERSION` unchanged; doc-only patch.
- **`family_id` scope boundary** (`python/bundle_spec/taxonomy.py` docstring + `README.md`): documented that `family_id` is the **validator / node-software** vulnerability-class vocabulary, and that on-chain contract / DeFi-economic findings (AMM value-extraction, oracle/TWAP manipulation, liquidation, governance capture, bridge-contract message abuse, ERC-4626/reentrancy logic) are **out of scope**: a separate DeFi-economic namespace, never `family_id` values, cross-mapped in the VII `ledger/family_map.csv`. Records the canonical-vocabulary ratification (the 11-value `FamilyId` enum, unchanged). **No enum/wire change**: `BUNDLE_VERSION` unchanged; doc-only patch.

## [0.1.1] — 2026-05-13
## [0.1.1] - 2026-05-13

### Clarified

Expand All @@ -21,7 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- **`tools/validate_bundle.py`**: release-cert validator CLI that walks bundle directories + asserts each manifest validates against `BundleManifest` + asserts flag-honest file presence per the forward-only `BundleFiles` semantic. Per-bundle release-cert JSON output when `--cert-out` supplied. Smoke-tested against the 5 reference example bundles (5/5 pass).

## [0.1.0] — 2026-05-06
## [0.1.0] - 2026-05-06

### Added

Expand All @@ -39,33 +39,33 @@ Initial release of Bundle v1.
`arrow_schema::Schema` constants), same `compute_genome_id`
output (verified by shared test vectors). JSON Schema embedded
via `include_str!()`.
- **JSON Schema artefacts** in `schema/` — generated from the
- **JSON Schema artefacts** in `schema/`, generated from the
Pydantic models via `tools/regen_schema.py`. Stable formatter
(sorted keys, 2-space indent, trailing newline). CI verifies
no drift between committed schema and Pydantic source.
- **Five reference example bundles** in `examples/` —
- **Five reference example bundles** in `examples/`:
`sui_F10_multi_get_objects_amp` (attack),
`sui_F14_devinspect_tokio_wedge` (attack),
`sui_BENIGN_reproducer_pipeline` (benign),
`SOL_F10_multi_get_accounts_amp` (attack),
`solana_BENIGN_organic_rpc` (benign). All `lab-tls-fronted`
fidelity, `self-owned` authorisation, with post-term cleartext
pcap stripped per the public-release audit.
- **`tools/regen_schema.py`** — regenerates JSON Schema from the
- **`tools/regen_schema.py`**: regenerates JSON Schema from the
canonical Pydantic models.
- **`tools/strip_pcap.py`** — drops post-term cleartext pcap from
- **`tools/strip_pcap.py`**: drops post-term cleartext pcap from
a bundle and re-emits the manifest with `packets_pcap=False`,
for safe public release of lab-tls-fronted bundles.

### Stability commitment

- **0.1.x**: bug-fix-only patches on the current schema.
- **0.2.0**: reserved for the upcoming `provenance.substrate` ×
`provenance.traffic_origin` decomposition (additive — existing
`provenance.traffic_origin` decomposition (additive: existing
`provenance.fidelity_class` becomes derived). Tracked as the
*next test of additive extensibility* in upstream methodology
documentation.
- **1.0.0**: deferred to first set of external citations — not
- **1.0.0**: deferred to first set of external citations, not
calendar-bound.

### Cross-language guarantees pinned by tests
Expand Down
45 changes: 26 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,30 @@ This repository contains the **canonical normative specification**

```
<corpus_id>/
├── manifest.json — BundleManifest (Pydantic-validated)
├── packets.pcap — packet capture, snaplen-256 by default
├── host.parquet — host telemetry time-series
├── app.parquet — application metric scrape time-series
├── protocol.parquet — consensus / protocol signal time-series
└── responses.parquet — per-request response semantics
├── manifest.json - BundleManifest (Pydantic-validated)
├── packets.pcap - packet capture, snaplen-256 by default
├── host.parquet - host telemetry time-series
├── app.parquet - application metric scrape time-series
├── protocol.parquet - consensus / protocol signal time-series
└── responses.parquet - per-request response semantics
```

License: **MIT**. The format is intended to be adopted on third-party
data without coordination — a researcher can produce bundles against
data without coordination. A researcher can produce bundles against
their own infrastructure, train against them, and publish results
citing the format. Adoption is the point.

## Adopting the standard

- [Adoption guide](docs/adoption-guide.md): how to classify a validator-integrity
finding into the eleven-family vocabulary.
- [Methodology](docs/methodology.md): how the Validator Integrity Index is built,
and what it does and does not yet claim.

## Why a format?

Adversarial research on blockchain infrastructure has historically
been one-off — each project builds its own capture pipeline, its own
been one-off. Each project builds its own capture pipeline, its own
labelling conventions, its own evaluation methodology. That makes
results incomparable.

Expand Down Expand Up @@ -105,16 +112,16 @@ Identity, taxonomy, time window, attack parameters, provenance.
```

The `family_id` is a **chain-agnostic** vulnerability-class label
(11 values: 10 attack families + benign) — **validator / node-software
classes only**; on-chain contract / DeFi-economic findings are a
(11 values: 10 attack families + benign), covering **validator / node-software
classes only**. On-chain contract / DeFi-economic findings are a
separate out-of-scope namespace (cross-mapped in the VII
`ledger/family_map.csv`). The `primitive_id` is the
**chain-specific** implementation. This two-level taxonomy is the
load-bearing decomposition for cross-chain holdout.

### packets.pcap

Raw packet capture. Default snaplen 256 bytes (TCP/IP headers only —
Raw packet capture. Default snaplen 256 bytes (TCP/IP headers only, so
no payload bytes leak). Producers can use larger snaplens when they
control the privacy posture.

Expand All @@ -134,7 +141,7 @@ All four files share a monotonic `t_ns` column (nanoseconds since

Schemas are stable; new columns require a `BUNDLE_VERSION` bump.

## Quick start — Python
## Quick start: Python

```bash
pip install bundle-spec
Expand All @@ -160,7 +167,7 @@ table = pq.read_table("crp_abc123/responses.parquet")
assert table.schema.equals(responses_schema()) # or a superset, additive
```

## Quick start — Rust
## Quick start: Rust

```toml
[dependencies]
Expand All @@ -180,7 +187,7 @@ println!("{} {} {:?}", m.family_id, m.primitive_id, m.ground_truth_label);

See `python/bundle_spec/bundle_v1.py` (Pydantic models with
docstrings) and `schema/bundle_v1.schema.json` (generated). Both are
the same content in different forms — the Pydantic model is
the same content in different forms. The Pydantic model is
canonical.

Required structured-enum fields:
Expand Down Expand Up @@ -213,7 +220,7 @@ must be byte-for-byte equivalent. CI runs
`tools/compare_arrow_schemas.py` to check.

The `genome_id` canonicalisation (sorted-key JSON, SHA-256, first 16
hex chars) is also pinned by a cross-language determinism test — a
hex chars) is also pinned by a cross-language determinism test: a
fixed input must produce the same hex string in both languages.

## Examples
Expand Down Expand Up @@ -247,12 +254,12 @@ re-typing, removing) require a major bump and a new module path

SemVer for `bundle-spec` (this package):

- **0.1.x** — bug-fix-only patches on the current schema.
- **0.2.0** — reserved for the upcoming `provenance.substrate` ×
- **0.1.x**: bug-fix-only patches on the current schema.
- **0.2.0**: reserved for the upcoming `provenance.substrate` ×
`provenance.traffic_origin` decomposition (additive: existing
`provenance.fidelity_class` will be derived from the new pair via
a model validator; existing readers stay compatible).
- **1.0.0** — deferred to first set of external citations.
- **1.0.0**: deferred to first set of external citations.

## Reproducibility

Expand All @@ -277,6 +284,6 @@ network dependency.

## License

MIT — see `LICENSE`. Code, schemas, and example metadata are MIT-
MIT. See `LICENSE`. Code, schemas, and example metadata are MIT-
licensed and free to use without restriction. The example bundle
**data** (Parquet files, packet captures) is also MIT-licensed.
74 changes: 74 additions & 0 deletions docs/adoption-guide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Adopting the standard: classify a validator-integrity finding

The Bundle v1 format and its family vocabulary are published so that adversarial research on validator
infrastructure becomes comparable: one on-disk shape, one labelling scheme, one evaluation methodology,
adoptable on your own data without coordination. This guide shows how to classify a node or client
integrity finding into the vocabulary. It is deliberately short.

## The eleven families

| family | what it is | how to tell it apart |
|---|---|---|
| `response_amp` | a small request that returns disproportionately large response bytes | the cost lands on the server's outbound bandwidth |
| `compute_amp` | a small request that triggers disproportionate server-side CPU | parser pathologies, verification fallbacks, sync-over-async wedges |
| `memory_amp` | unbounded accumulation of retained state in process memory | it grows over time, not per request |
| `connection_exhaustion` | a per-connection or per-subscription resource that is never released on disconnect | the leak is the open handle, not the payload |
| `consensus_abuse` | validator-authenticated misbehaviour inside consensus | it presupposes validator key material |
| `gossip_abuse` | unauthenticated peer-to-peer or gossip abuse via public peer endpoints | state-sync floods, request amplification |
| `auth_bypass` | missing or broken authentication on a control, admin, or first-party API | the surface should have required credentials |
| `rate_limiter_bypass` | a defect in the rate-limiter's own logic | fail-open, burst-doubling, or key-collision, not a response-size issue |
| `service_misconfig` | exploitation of a misconfigured co-located daemon | Redis, Grafana, Prometheus, SSH, and similar |
| `reconnaissance` | discovery and target-mapping traffic itself | it is the prior stage, not the exploit |
| `benign` | no attack shape | it trains the "not malicious" boundary |

## How to classify a finding

Work top to bottom. The first rule that matches gives the family.

1. If it is only discovery or mapping traffic, with no exploitation, it is `reconnaissance`.
2. If it requires validator key material to misbehave inside consensus, it is `consensus_abuse`.
3. If it is unauthenticated abuse of a public peer-to-peer or gossip endpoint, it is `gossip_abuse`.
4. If it defeats authentication on a surface that should need credentials, it is `auth_bypass`.
5. If the bug is in the rate-limiter's own logic, it is `rate_limiter_bypass`.
6. If it is a misconfigured co-located service being exploited, it is `service_misconfig`.
7. Otherwise it is a resource-amplification shape. Pick by the resource that dominates: large response
bytes from a small request is `response_amp`; large CPU from a small request is `compute_amp`;
unbounded retained memory over time is `memory_amp`; a per-connection or per-subscription leak is
`connection_exhaustion`.
Comment on lines +34 to +37

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The phrasing "large response bytes" and "large CPU" is slightly awkward. Rephrasing these to refer to "large response size" and "high CPU consumption" would improve the grammatical flow and clarity of the classification rules.

Suggested change
7. Otherwise it is a resource-amplification shape. Pick by the resource that dominates: large response
bytes from a small request is `response_amp`; large CPU from a small request is `compute_amp`;
unbounded retained memory over time is `memory_amp`; a per-connection or per-subscription leak is
`connection_exhaustion`.
7. Otherwise it is a resource-amplification shape. Pick by the resource that dominates: a large response
size (in bytes) from a small request is `response_amp`; high CPU consumption from a small request is `compute_amp`;
unbounded retained memory over time is `memory_amp`; a per-connection or per-subscription leak is
`connection_exhaustion`.

8. If there is no attack shape, it is `benign`.

A finding can touch more than one mechanism. Label it by the dominant one, meaning the mechanism the
mitigation lives in. A response-size cap fixes `response_amp`; a fix in the limiter logic is
`rate_limiter_bypass`.

## A worked example, using a public NullRabbit advisory

Take advisory NR-2026-001 (Solana). An unauthenticated `getMultipleAccounts` JSON-RPC call returns far
more response bytes than the request costs to send, so one caller can drive disproportionate server
egress.

Walk the rules in order. Is it discovery only? No. Does it need consensus key material? No. Is it
peer-to-peer or gossip? No, it is the public JSON-RPC surface. Does it defeat authentication? No. Is it a
rate-limiter logic bug? No. Is it a co-located service? No. It is a resource-amplification shape, and the
dominant cost is response bytes, so it is `response_amp`.

For contrast, the same advisory's `simulateTransaction` finding drives disproportionate CPU rather than
bytes, so it is `compute_amp`. Same endpoint family, different family label, because the dominant resource
differs. That is the discipline the vocabulary is meant to enforce.

## Producing bundles

To contribute measurements, capture each run in the Bundle v1 shape: a manifest, a packet capture, and the
four time-series modalities (host, app, protocol, responses), as specified in this repository. The public
sample dataset provides ground-truth examples to validate your pipeline against.

## Citing and contributing

Cite the format using the `CITATION.cff` file in this repository. The format is MIT-licensed, so you can

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The repository does not currently contain a CITATION.cff file. To prevent broken references, please update this text to refer to the Citation section in the README.md, or add a CITATION.cff file to the repository root.

Suggested change
Cite the format using the `CITATION.cff` file in this repository. The format is MIT-licensed, so you can
Cite the format using the citation details provided in the `README.md` of this repository. The format is MIT-licensed, so you can

adopt it on your own infrastructure and publish results citing it. To propose a new family or refine a
definition, open an issue. The vocabulary is versioned and evolves by proposal, not by decree.

## Scope

This vocabulary is for node and client software findings only. On-chain contract, protocol, and
DeFi-economic findings use a separate vocabulary and are out of scope here.
70 changes: 70 additions & 0 deletions docs/methodology.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# How the Validator Integrity Index is built

*A companion to the Bundle v1 format specification and the NullRabbit disclosure record.*

NullRabbit Labs measures one thing: the probability that a given blockchain validator or node client
commits a slashable fault or is remotely exploitable. This note explains how the underlying finding
record is built, and just as importantly, what the index does and does not yet claim. A rating layer is
either trusted or it is nothing, so the method and its honest limits are the product.

## 1. Canonicalise, don't count

Adversarial research surfaces the same underlying weakness many times: across chains, across ports, and
across client implementations. Counting each surfacing as a separate finding inflates a number without
adding information. Instead, one written rule collapses repeat surfacings of the same root weakness into a
single canonical record, keyed to the specific client and component. The index therefore measures
distinct, real weaknesses rather than research volume.

## 2. Provenance: ours, or a reproduction of someone else's public disclosure

Every record is marked as one of two kinds. The first is original, meaning a weakness NullRabbit
discovered or first measured. The second is public-replication, meaning a faithful reproduction of an
already-public disclosure such as a CVE, a GHSA advisory, or a named third-party audit. The distinction is
explicit, so that a re-statement of public work is never mistaken for a novel finding, and so the line
between what is disclosure-gated and what is freely publishable is never in doubt.

## 3. One vocabulary for every finding

Every weakness is classified into one of eleven chain-agnostic families defined by the Bundle v1 format:
response amplification, compute amplification, memory amplification, connection exhaustion, consensus
abuse, gossip abuse, authentication bypass, rate-limiter bypass, service misconfiguration, reconnaissance,
and a benign class. The vocabulary is defined by mechanism, not by chain. A finding in a Solana RPC path
and a structurally identical finding in a Cosmos RPC path receive the same family label. That is what
makes cross-client comparison meaningful, and what lets a finding reported by an outside team drop into
the same scheme. The [adoption guide](adoption-guide.md) shows how to classify a finding.

## 4. The disclosure record, including the rejections

For every disclosed finding, NullRabbit records what was sent to the vendor and what the vendor actually
said back. That includes the disclosures that were ignored, dismissed, or declined, and the findings
NullRabbit itself later retracted. This record is published deliberately. Showing where vendors
disagreed, where they went silent, and where we were wrong is precisely what makes the rest of the record
credible.

## 5. What the index does not claim

Honesty about limits is part of the method, not a footnote.

First, the index is procedural, not yet empirical. Today its credibility rests on a clean, documented
method and an honest disclosure record, not on a large body of vendor-confirmed, externally-credited
validations. Those accrue over time as disclosures are acknowledged. Until then, the per-client ratings
are not presented as externally proven.

Second, there is no cross-chain generalisation claim. The research behind the index does not yet reliably
generalise a detector trained on one chain to an unseen chain. That limitation is reported plainly, and
in-distribution results are never presented as cross-chain ones.

Third, self-graded severity is a liability, not an asset. Where a severity is our own assessment rather
than a vendor's, it is treated as provisional and calibrated against the disclosed evidence.

## 6. Scope

The index covers node and client software only: the daemon a validator runs, spanning consensus, RPC,
peer-to-peer, and co-located services. On-chain contract, protocol, and DeFi-economic findings are a
separate measurement with a separate vocabulary, and they are deliberately not folded in.

## Where the standard lives

The Bundle v1 format and the family vocabulary are published under the MIT license in this repository. The
format is designed to be adopted on third-party data without coordination. See the
[adoption guide](adoption-guide.md).
Loading
Loading