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
27 changes: 26 additions & 1 deletion docs/api/cbor.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,12 @@ <h2>Exports</h2>
<li><a href="#CBOR_TAG">CBOR_TAG</a></li>
<li><a href="#CBOR_SIMPLE">CBOR_SIMPLE</a></li>
<li><a href="#CBOR_FLOAT">CBOR_FLOAT</a></li>
<li><a href="#CborDecodeOptions">CborDecodeOptions</a></li>
<li><a href="#default_decode_options">default_decode_options</a></li>
<li><a href="#parse">parse</a></li>
<li><a href="#parse_with">parse_with</a></li>
<li><a href="#encode">encode</a></li>
<li><a href="#encode_deterministic">encode_deterministic</a></li>
<li><a href="#type">type</a></li>
<li><a href="#is_null">is_null</a></li>
<li><a href="#is_undefined">is_undefined</a></li>
Expand Down Expand Up @@ -196,18 +200,39 @@ <h3 class="function-name">CBOR_SIMPLE <span class="kind">const</span></h3>
<h3 class="function-name">CBOR_FLOAT <span class="kind">const</span></h3>
<pre class="usage"><code>CBOR_FLOAT = 7</code></pre>
</div>
<div class="function" id="CborDecodeOptions" data-name="CborDecodeOptions">
<h3 class="function-name">CborDecodeOptions <span class="kind">struct</span></h3>
<pre class="usage"><code>struct CborDecodeOptions</code></pre>
<p class="doc">What parse_with accepts. default_decode_options() is what parse uses: safe for input from an untrusted peer. The element and pair defaults are fxamacker/cbor's.</p>
</div>
<div class="function" id="default_decode_options" data-name="default_decode_options">
<h3 class="function-name">default_decode_options <span class="kind">fn</span></h3>
<pre class="usage"><code>default_decode_options() <span class="arrow">-&gt;</span> CborDecodeOptions</code></pre>
</div>
<div class="function" id="parse" data-name="parse">
<h3 class="function-name">parse <span class="kind">fn</span></h3>
<pre class="usage"><code>parse(cbor_data) <span class="arrow">-&gt;</span> ptr!</code></pre>
<p class="signature"><span class="sig-params">cbor_data: string</span></p>
<p class="doc">Parse one CBOR data item that is the whole of `cbor_data`. Returns (value_ptr, &quot;&quot;) on success, (null, error) on failure. Caller owns the returned value and must free with cbor.free. Bytes left after the item are an error (#2470): `0102` used to parse as 1.</p>
<p class="doc">parse_with under default_decode_options(): duplicate map keys refused, 131072 elements or pairs per container, nesting to 256.</p>
</div>
<div class="function" id="parse_with" data-name="parse_with">
<h3 class="function-name">parse_with <span class="kind">fn</span></h3>
<pre class="usage"><code>parse_with(cbor_data, opts) <span class="arrow">-&gt;</span> ptr!</code></pre>
<p class="signature"><span class="sig-params">cbor_data: string, opts: CborDecodeOptions</span></p>
<p class="doc">Parse one CBOR data item that is the whole of `cbor_data`, under `opts`. Returns (value_ptr, &quot;&quot;) on success, (null, error) on failure. Caller owns the returned value and must free with cbor.free. Bytes left after the item are an error (#2470): `0102` used to parse as 1.</p>
</div>
<div class="function" id="encode" data-name="encode">
<h3 class="function-name">encode <span class="kind">fn</span></h3>
<pre class="usage"><code>encode(value) <span class="arrow">-&gt;</span> string!</code></pre>
<p class="signature"><span class="sig-params">value: ptr</span></p>
<p class="doc">Serialize a CBOR value tree to a binary string. Returns (encoded_bytes, &quot;&quot;) on success, (&quot;&quot;, error) on failure.</p>
</div>
<div class="function" id="encode_deterministic" data-name="encode_deterministic">
<h3 class="function-name">encode_deterministic <span class="kind">fn</span></h3>
<pre class="usage"><code>encode_deterministic(value) <span class="arrow">-&gt;</span> string!</code></pre>
<p class="signature"><span class="sig-params">value: ptr</span></p>
<p class="doc">Deterministic CBOR encoding (RFC 8949 §4.2 Core Deterministic Encoding).</p>
</div>
<div class="function" id="type" data-name="type">
<h3 class="function-name">type <span class="kind">fn</span></h3>
<pre class="usage"><code>type(value) <span class="arrow">-&gt;</span> int</code></pre>
Expand Down
6 changes: 5 additions & 1 deletion docs/api/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ <h3>casper</h3>
<a href="cbor.html" class="module-card">
<h3>cbor</h3>
<p>MIT License (https://opensource.org/licenses/MIT) Portions copyright (c) 2019-present Faye Amacker (https://github.com/fxamacker/cbor) Portions copyright (c) 2026 Aether Developers.</p>
<span class="count">46 exports</span>
<span class="count">50 exports</span>
</a>
<a href="clapae.html" class="module-card">
<h3>clapae</h3>
Expand Down Expand Up @@ -813,8 +813,12 @@ <h2>Search Results</h2>
<li class="function-item" data-name="CBOR_TAG"><a href="cbor.html#CBOR_TAG"><span class="fn-name">CBOR_TAG</span><span class="fn-module">cbor</span></a></li>
<li class="function-item" data-name="CBOR_SIMPLE"><a href="cbor.html#CBOR_SIMPLE"><span class="fn-name">CBOR_SIMPLE</span><span class="fn-module">cbor</span></a></li>
<li class="function-item" data-name="CBOR_FLOAT"><a href="cbor.html#CBOR_FLOAT"><span class="fn-name">CBOR_FLOAT</span><span class="fn-module">cbor</span></a></li>
<li class="function-item" data-name="CborDecodeOptions"><a href="cbor.html#CborDecodeOptions"><span class="fn-name">CborDecodeOptions</span><span class="fn-module">cbor</span></a></li>
<li class="function-item" data-name="default_decode_options"><a href="cbor.html#default_decode_options"><span class="fn-name">default_decode_options</span><span class="fn-module">cbor</span></a></li>
<li class="function-item" data-name="parse"><a href="cbor.html#parse"><span class="fn-name">parse</span><span class="fn-module">cbor</span></a></li>
<li class="function-item" data-name="parse_with"><a href="cbor.html#parse_with"><span class="fn-name">parse_with</span><span class="fn-module">cbor</span></a></li>
<li class="function-item" data-name="encode"><a href="cbor.html#encode"><span class="fn-name">encode</span><span class="fn-module">cbor</span></a></li>
<li class="function-item" data-name="encode_deterministic"><a href="cbor.html#encode_deterministic"><span class="fn-name">encode_deterministic</span><span class="fn-module">cbor</span></a></li>
<li class="function-item" data-name="type"><a href="cbor.html#type"><span class="fn-name">type</span><span class="fn-module">cbor</span></a></li>
<li class="function-item" data-name="is_null"><a href="cbor.html#is_null"><span class="fn-name">is_null</span><span class="fn-module">cbor</span></a></li>
<li class="function-item" data-name="is_undefined"><a href="cbor.html#is_undefined"><span class="fn-name">is_undefined</span><span class="fn-module">cbor</span></a></li>
Expand Down
2 changes: 1 addition & 1 deletion docs/stdlib-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ header comment is the authoritative description.
| `std.capsicum` | FreeBSD Capsicum capability-mode bindings. | 28 | [guide](../std/capsicum/README.md) · [source](../std/capsicum/module.ae) |
| `std.cas` | Content-addressed store keyed by the sha256 of file contents. | 7 | [guide](../std/cas/README.md) · [source](../std/cas/module.ae) |
| `std.casper` | FreeBSD Casper service delegation. | 8 | [guide](../std/casper/README.md) · [source](../std/casper/module.ae) |
| `std.cbor` | CBOR encoding and decoding (RFC 8949). | 46 | [guide](../std/cbor/README.md) · [source](../std/cbor/module.ae) |
| `std.cbor` | CBOR encoding and decoding (RFC 8949). | 50 | [guide](../std/cbor/README.md) · [source](../std/cbor/module.ae) |
| `std.clapae` | Command-line argument parser, modelled on clap. | 32 | [guide](../std/clapae/README.md) · [source](../std/clapae/module.ae) |
| `std.collections` | Dynamic list, hash map and packed int array, with the raw externs the alias modules re-export. | 44 | [guide](../std/collections/README.md) · [source](../std/collections/module.ae) |
| `std.config` | Process-global immutable string to string store. | 6 | [guide](../std/config/README.md) · [source](../std/config/module.ae) |
Expand Down
13 changes: 13 additions & 0 deletions new_changelogs/20261029T182754Z-security-cbor-decode-limits.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
- **`std.cbor` decodes untrusted input safely by default.** `parse` now
refuses duplicate map keys (`cbor: duplicate map key "grant"`), where two
keys are duplicates when they are the same value however they were encoded,
and limits each array or map to 131072 elements or pairs. It also refuses a
declared length or count that cannot fit in the bytes left before
allocating for it. `parse_with(data, opts)` takes a `CborDecodeOptions`
(`default_decode_options()` gives the defaults) to change the limits, allow
duplicates, or refuse indefinite-length items. `encode_deterministic`
writes RFC 8949 §4.2.1 core deterministic encoding: shortest forms, definite
lengths and sorted map keys. Duplicate detection and key sorting are
linear-logarithmic, so a map at the limit cannot be used to spend minutes
of CPU. Refusing duplicate keys is a change to `parse`'s behaviour; use
`parse_with` with `allow_duplicate_keys = 1` for the old one.
42 changes: 39 additions & 3 deletions std/cbor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,47 @@ and argument sizes. A value built with the constructors encodes in preferred
serialization: the shortest head, and a float in the narrowest width that
holds it exactly (`num(1.5)` is `f93e00`).

### Decode Options

`parse_with(data, opts)` parses input with configurable decoder bounds and policy options:

```aether,fragment
struct CborDecodeOptions {
max_depth: int // default 256
max_array_elements: int // default 131072
max_map_pairs: int // default 131072
allow_duplicate_keys: int // default 0 (refuse)
allow_indefinite: int // default 1
}
default_decode_options() -> CborDecodeOptions
parse_with(cbor_data: string, opts: CborDecodeOptions) -> ptr!
```

`parse(data)` calls `parse_with(data, default_decode_options())`. **By default, `parse` refuses duplicate map keys** (`cbor: duplicate map key ...`), preventing parser-differential vulnerabilities.

- **Duplicate keys:** Two keys are duplicates when their decoded key values are equal as values (e.g. text key `"a"` encoded definite vs indefinite). Integer `1` and text `"1"` remain distinct keys.
- **Declared bounds & limits:** Before allocating or looping, declared string lengths, array element counts, and map pair counts are checked against remaining input bytes and option limits (`max_array_elements`, `max_map_pairs`).
- **Indefinite items:** Setting `allow_indefinite = 0` refuses indefinite-length strings, arrays, and maps (`cbor: indefinite-length items are disabled`).

### Deterministic Encoding

```aether,fragment
encode_deterministic(value: ptr) -> string!
```

`encode_deterministic` encodes values using RFC 8949 §4.2 Core Deterministic Encoding rules:
- Preferred serialization: shortest argument heads and narrowest exact float widths (half, single, double).
- Re-encodes indefinite-length strings, arrays, and maps as definite-length items.
- Map keys are sorted in the bytewise lexicographic order of their deterministic encodings.
- A map with duplicate keys returns an encoding error rather than silently encoding.
- Use `encode_deterministic` when CBOR messages must be hashed, signed, or compared byte-exact. `encode` retains its byte-for-byte roundtrip behavior.

`parse` treats its input as untrusted, and reads one data item that must be
the whole input. It refuses, with an error rather than a value:

- bytes after the item (`cbor: trailing bytes after the data item`): `0102`
is not 1;
- duplicate map keys (`cbor: duplicate map key ...`);
- anything that is not well-formed (RFC 8949 §3, every case of Appendix F):
a reserved additional-information value, a break outside an
indefinite-length item or where a map value belongs
Expand All @@ -72,8 +108,7 @@ the whole input. It refuses, with an error rather than a value:
bytes, a truncated head or body;
- a text string, or a text chunk, that is not UTF-8
(`cbor: invalid UTF-8 in text string`);
- nesting deeper than 256 levels, the same limit as `std.json`
(`cbor: nesting deeper than 256`);
- nesting deeper than `max_depth` levels (default 256, the same limit as `std.json`);
- a string length, or an array or map count, larger than the bytes left in
the input, 2^63 and up included (`cbor: unexpected EOF`).

Expand All @@ -96,6 +131,7 @@ specifies it.

## Exports

`parse`, `encode`, `diagnose`, `from_int`, `from_uint`, `from_nint`, `num`,
`parse`, `parse_with`, `CborDecodeOptions`, `default_decode_options`, `encode`,
`encode_deterministic`, `diagnose`, `from_int`, `from_uint`, `from_nint`, `num`,
`str`, `arr`, `obj`, `boolean`, `null_value`, `get_long`, `get_int64`,
`get_uint`, `get_nint`, `free`, and the `CBOR_*` type constants.
Loading
Loading