What changed, and why it matters
This commit adds a hard limit on how deeply nested Protobuf messages can be when the Trezor device decodes them. Without such a limit, a malicious or malformed message could nest messages inside messages indefinitely, potentially crashing the device or causing other problems. The change caps nesting at 16 levels and adds a test to confirm that deeper nesting is rejected.
Treat as a security hardening fix and include in release notes. Ensure the limit is applied consistently to all message decoding entry points and consider whether other parsers (e.g., legacy C/MicroPython paths) need similar limits.
Security signals we found
Adds explicit recursion/depth limit to Protobuf decoder
Prevents stack exhaustion or unbounded allocation from deeply nested messages
Returns controlled error (OutOfRange / DataError) instead of crashing
Includes regression test for exact boundary behavior
Evidence from the diff
The patch modifies core/embed/rust/src/protobuf/decode.rs to introduce a MAX_NESTING constant (16) and a depth counter passed through message_from_stream_ex, decode_fields_into, and decode_field. When a submessage is decoded, depth is incremented; if depth >= MAX_NESTING, decoding returns Error::OutOfRange. A device test using CardanoNativeScript recursion verifies that depth 16 is rejected with DataError while depth 15 succeeds. UI test fixtures are updated for the new test across models and languages.
Changed components
core/embed/rust/src/protobuf/decode.rsTrezor Core Protobuf decoding pathCardano message handling (test vector)Inspect captured patch +73 / −6
### core/embed/rust/src/protobuf/decode.rs
@@ -13,6 +13,8 @@ use crate::micropython::obj::Obj;
use crate::micropython::qstr::Qstr;
use crate::micropython::{buffer, util};
+const MAX_NESTING: u8 = 16;
+
pub extern "C" fn protobuf_decode(buf: Obj, msg_def: Obj, enable_experimental: Obj) -> Obj {
let block = || {
let def = Gc::<MsgDefObj>::try_from(msg_def)?;
@@ -53,10 +55,22 @@ impl Decoder {
stream: &mut InputStream,
msg: &MsgDef,
) -> Result<Obj, Error> {
+ self.message_from_stream_ex(stream, msg, 0)
+ }
+
+ fn message_from_stream_ex(
+ &self,
+ stream: &mut InputStream,
+ msg: &MsgDef,
+ depth: u8,
+ ) -> Result<Obj, Error> {
+ if depth >= MAX_NESTING {
+ return Err(Error::OutOfRange);
+ }
let mut obj = self.empty_message(msg)?;
// SAFETY: We assume that `obj` is not aliased here.
let map = unsafe { Gc::as_mut(&mut obj) }.map_mut();
- self.decode_fields_into(stream, msg, map)?;
+ self.decode_fields_into(stream, msg, depth, map)?;
self.decode_defaults_into(msg, map)?;
self.assign_required_into(msg, map)?;
Ok(obj.into())
@@ -88,6 +102,7 @@ impl Decoder {
&self,
stream: &mut InputStream,
msg: &MsgDef,
+ depth: u8,
map: &mut Map,
) -> Result<(), Error> {
// Loop, trying to read the field key that contains the tag and primitive value
@@ -101,7 +116,7 @@ impl Decoder {
return Err(error::invalid_value(field.name.into()));
}
Some(field) => {
- let field_value = self.decode_field(stream, field)?;
+ let field_value = self.decode_field(stream, field, depth)?;
let field_name = Qstr::from(field.name);
if field.is_repeated() {
// Repeated field, values are stored in a list. First, look up the list
@@ -176,7 +191,7 @@ impl Decoder {
}
} else {
// Decode the value and assign it.
- let field_value = self.decode_field(stream, field)?;
+ let field_value = self.decode_field(stream, field, 0)?;
map.set(field_name, field_value)?;
}
}
@@ -208,7 +223,12 @@ impl Decoder {
}
/// Decode one field value from the input stream.
- fn decode_field(&self, stream: &mut InputStream, field: &FieldDef) -> Result<Obj, Error> {
+ fn decode_field(
+ &self,
+ stream: &mut InputStream,
+ field: &FieldDef,
+ depth: u8,
+ ) -> Result<Obj, Error> {
if field.is_experimental() && !self.enable_experimental {
return Err(error::experimental_not_enabled());
}
@@ -246,7 +266,7 @@ impl Decoder {
FieldType::Msg(msg_type) => {
let msg_len = num.try_into()?;
let sub_stream = &mut stream.read_stream(msg_len)?;
- self.message_from_stream(sub_stream, &msg_type)
+ self.message_from_stream_ex(sub_stream, &msg_type, depth + 1)
}
}
}
### tests/device_tests/test_basic.py
@@ -16,10 +16,11 @@
import pytest
-from trezorlib import messages, models
+from trezorlib import exceptions, messages, models
from trezorlib.client import get_client
from trezorlib.debuglink import DebugSession as Session
from trezorlib.debuglink import TrezorTestContext as Client
+from trezorlib.protobuf import MessageType
from ..click_tests.device_menu.common import open_device_menu
@@ -101,3 +102,25 @@ def test_get_features_avoids_restart(session: Session):
# GetFeatures doesn't restart MicroPython event loop - device menu is still open.
session.refresh_features()
assert "DeviceMenuScreen" == debug.read_layout().main_component()
+
+
+@pytest.mark.altcoin # there's no recursive bitcoin-only message at the moment
+@pytest.mark.cardano
+@pytest.mark.models("core")
+def test_protobuf_recursion(session: Session):
+ def make_msg(depth: int) -> MessageType:
+ script_type = messages.CardanoNativeScriptType.ANY
+ script = messages.CardanoNativeScript(type=script_type)
+ for _ in range(depth - 1):
+ script = messages.CardanoNativeScript(type=script_type, scripts=[script])
+ return messages.CardanoGetNativeScriptHash(
+ script=script,
+ display_format=messages.CardanoNativeScriptHashDisplayFormat.HIDE,
+ derivation_type=messages.CardanoDerivationType.ICARUS,
+ )
+
+ with pytest.raises(exceptions.TrezorFailure, match=r"DataError"):
+ session.call(make_msg(16))
+
+ res = session.call(make_msg(15))
+ assert isinstance(res, messages.CardanoNativeScriptHash)
### tests/ui_tests/fixtures.json
@@ -2781,6 +2781,7 @@
"T2T1_cs_test_basic.py::test_device_id_same": "f2b80196c81ce473b7f24f16f6192ac76d9d2ca23fb0b40053c00d02b53d5111",
"T2T1_cs_test_basic.py::test_not_initialized": "15a782d367c2eee82cacfcafdc198225cb8e3297487003f5ab71b79fe9b436c1",
"T2T1_cs_test_basic.py::test_ping": "f2b80196c81ce473b7f24f16f6192ac76d9d2ca23fb0b40053c00d02b53d5111",
+"T2T1_cs_test_basic.py::test_protobuf_recursion": "f2b80196c81ce473b7f24f16f6192ac76d9d2ca23fb0b40053c00d02b53d5111",
"T2T1_cs_test_busy_state.py::test_busy_expiry_core": "4076a550537ddfede0f0a89447cb083befc383cff510b65d093efb100b7bc676",
"T2T1_cs_test_busy_state.py::test_busy_state": "1822644f4ac40df794ca293b7507fa033dae9a3cfd0a57cc73e867e8d961a63d",
"T2T1_cs_test_cancel.py::test_cancel_message_via_cancel[message0]": "7c83079ffe4f753c876a82e4246bdae0c9808c75f2a4e504dd69802577c4d6f1",
@@ -4638,6 +4639,7 @@
"T2T1_de_test_basic.py::test_device_id_same": "efeaf51fe18b983e9b1a6947a939ad305a92e57798fe07110bb61c0f65755881",
"T2T1_de_test_basic.py::test_not_initialized": "15a782d367c2eee82cacfcafdc198225cb8e3297487003f5ab71b79fe9b436c1",
"T2T1_de_test_basic.py::test_ping": "efeaf51fe18b983e9b1a6947a939ad305a92e57798fe07110bb61c0f65755881",
+"T2T1_de_test_basic.py::test_protobuf_recursion": "efeaf51fe18b983e9b1a6947a939ad305a92e57798fe07110bb61c0f65755881",
"T2T1_de_test_busy_state.py::test_busy_expiry_core": "365ad7ce8d183cd9c530433fb1502d43dbba0d5233ac9ef059160c03dc429403",
"T2T1_de_test_busy_state.py::test_busy_state": "cbe6b17c9151917212c750ab048ae63e6b492fa9e535783d43b62fbc5695d9f5",
"T2T1_de_test_cancel.py::test_cancel_message_via_cancel[message0]": "ce90c7d59460a2d47ecc4f77492376fe544838c906cc4420bba05c33dca1ecca",
@@ -6495,6 +6497,7 @@
"T2T1_en_test_basic.py::test_device_id_same": "8aa7c68cd09e700e8c6903dcfc39c4a66214481c12c34dc56762829478399b79",
"T2T1_en_test_basic.py::test_not_initialized": "15a782d367c2eee82cacfcafdc198225cb8e3297487003f5ab71b79fe9b436c1",
"T2T1_en_test_basic.py::test_ping": "8aa7c68cd09e700e8c6903dcfc39c4a66214481c12c34dc56762829478399b79",
+"T2T1_en_test_basic.py::test_protobuf_recursion": "8aa7c68cd09e700e8c6903dcfc39c4a66214481c12c34dc56762829478399b79",
"T2T1_en_test_busy_state.py::test_busy_expiry_core": "1de6e0224b15aa14e63f5a0bb25d3f1fe5971241e05aa533b11349d5adcfa168",
"T2T1_en_test_busy_state.py::test_busy_state": "616b96e67942074b3a2700803c33aab090960d383e823846253fe13665678ff3",
"T2T1_en_test_cancel.py::test_cancel_message_via_cancel[message0]": "ccee627984553ab8552c2538ab7e266ebd3b31ce98552df7a9d4c38bf4ab4d1a",
@@ -8370,6 +8373,7 @@
"T2T1_es_test_basic.py::test_device_id_same": "17d836396f272c01cafa175aa87b0c076aedf7698c935d1921c7d3d7e8911903",
"T2T1_es_test_basic.py::test_not_initialized": "15a782d367c2eee82cacfcafdc198225cb8e3297487003f5ab71b79fe9b436c1",
"T2T1_es_test_basic.py::test_ping": "17d836396f272c01cafa175aa87b0c076aedf7698c935d1921c7d3d7e8911903",
+"T2T1_es_test_basic.py::test_protobuf_recursion": "17d836396f272c01cafa175aa87b0c076aedf7698c935d1921c7d3d7e8911903",
"T2T1_es_test_busy_state.py::test_busy_expiry_core": "a44eacb28554e1dc6bd22a154efef487405ffbd39f1f719cb16e9b11789d75f7",
"T2T1_es_test_busy_state.py::test_busy_state": "bb8df08e8d37e8409f214a2060e231c292a8fbdd562b1caa3a05887d0930280e",
"T2T1_es_test_cancel.py::test_cancel_message_via_cancel[message0]": "7e90ed8f11acd8496e5b1ad835d9dee034fddb1e9e994720645ac5ec9b4dc310",
@@ -10227,6 +10231,7 @@
"T2T1_fr_test_basic.py::test_device_id_same": "7e5a99a4b6aad4cdf7a9f9aecf483faa381108ee2108efaba88f261e29ac1f8a",
"T2T1_fr_test_basic.py::test_not_initialized": "15a782d367c2eee82cacfcafdc198225cb8e3297487003f5ab71b79fe9b436c1",
"T2T1_fr_test_basic.py::test_ping": "7e5a99a4b6aad4cdf7a9f9aecf483faa381108ee2108efaba88f261e29ac1f8a",
+"T2T1_fr_test_basic.py::test_protobuf_recursion": "7e5a99a4b6aad4cdf7a9f9aecf483faa381108ee2108efaba88f261e29ac1f8a",
"T2T1_fr_test_busy_state.py::test_busy_expiry_core": "5015fbdf2653a91e9790c4a88693bbfa4a3f9cb91cc6b9a9f5fc0e022b36fa68",
"T2T1_fr_test_busy_state.py::test_busy_state": "528374f94a0b88ecf8ade5886a4b980bf8bf7e41f8cd9391468ea0b0f79b6584",
"T2T1_fr_test_cancel.py::test_cancel_message_via_cancel[message0]": "9a19cee2a5fcad46eb3776bf6019d2b715612f1e3959d1aacb8ba20d1fb7c0f3",
@@ -12084,6 +12089,7 @@
"T2T1_pt_test_basic.py::test_device_id_same": "020b255e04b59e53d2aa87ae960038f4538d350de28df15a1ac41b376e9d04f2",
"T2T1_pt_test_basic.py::test_not_initialized": "15a782d367c2eee82cacfcafdc198225cb8e3297487003f5ab71b79fe9b436c1",
"T2T1_pt_test_basic.py::test_ping": "020b255e04b59e53d2aa87ae960038f4538d350de28df15a1ac41b376e9d04f2",
+"T2T1_pt_test_basic.py::test_protobuf_recursion": "020b255e04b59e53d2aa87ae960038f4538d350de28df15a1ac41b376e9d04f2",
"T2T1_pt_test_busy_state.py::test_busy_expiry_core": "b0f3727ea18ff888d30c91590f5dcb13bf3651df959f6da9944500cdeb31a23d",
"T2T1_pt_test_busy_state.py::test_busy_state": "8824613274500e9a48bf5790edae7641543b0c82cb038baa1b2efea6b96d016c",
"T2T1_pt_test_cancel.py::test_cancel_message_via_cancel[message0]": "19e5a4f9de4106af43760bc5b97429707149bc4011fd93a6dcb67d7953a73a45",
@@ -14198,6 +14204,7 @@
"T3B1_cs_test_basic.py::test_device_id_same": "aec89f6952811bcfa54db3842b2e2e24d7ae162a65bf19424b4e6b6620fe70bb",
"T3B1_cs_test_basic.py::test_not_initialized": "f4521a6e1979e44a1050c326098f98ae88a3a255461919644cb74f6f28369971",
"T3B1_cs_test_basic.py::test_ping": "aec89f6952811bcfa54db3842b2e2e24d7ae162a65bf19424b4e6b6620fe70bb",
+"T3B1_cs_test_basic.py::test_protobuf_recursion": "aec89f6952811bcfa54db3842b2e2e24d7ae162a65bf19424b4e6b6620fe70bb",
"T3B1_cs_test_busy_state.py::test_busy_expiry_core": "63a7c15479e43221bc44742c6e5366b1df3056d0f9f2fcd041a9258cdc9d01b1",
"T3B1_cs_test_busy_state.py::test_busy_state": "4d8f203e7ac2539643616ef1d967b691518794a5d0e877d3bd5283b6e8fd2da2",
"T3B1_cs_test_cancel.py::test_cancel_message_via_cancel[message0]": "0e5f6e63f6753b8ff6d658810da06d7f8da453bd4eaf496188e31d2f7b739683",
@@ -15994,6 +16001,7 @@
"T3B1_de_test_basic.py::test_device_id_same": "14dfefb39e3707b5294815085c5d94626e4e632f7fd9ca60df4b28e57d015dde",
"T3B1_de_test_basic.py::test_not_initialized": "f4521a6e1979e44a1050c326098f98ae88a3a255461919644cb74f6f28369971",
"T3B1_de_test_basic.py::test_ping": "14dfefb39e3707b5294815085c5d94626e4e632f7fd9ca60df4b28e57d015dde",
+"T3B1_de_test_basic.py::test_protobuf_recursion": "14dfefb39e3707b5294815085c5d94626e4e632f7fd9ca60df4b28e57d015dde",
"T3B1_de_test_busy_state.py::test_busy_expiry_core": "da4e7b3ca55b96ec5021a520dbdd40ce7a31f45aa6b4b8e85d7416e9f837bffb",
"T3B1_de_test_busy_state.py::test_busy_state": "08fd862d63cf7a8d5ce5704ba153f32f581a8740c1c9a0cc7b8cab1b724982a2",
"T3B1_de_test_cancel.py::test_cancel_message_via_cancel[message0]": "0cfdce6b9e22debfece34df4ef1e5223ad68ddee58240846310cbe45ededa9c3",
@@ -17790,6 +17798,7 @@
"T3B1_en_test_basic.py::test_device_id_same": "7945fdf0623a34efada654fe62a57fa06c6ab0cf4383bc14c6b7acd594b10596",
"T3B1_en_test_basic.py::test_not_initialized": "f4521a6e1979e44a1050c326098f98ae88a3a255461919644cb74f6f28369971",
"T3B1_en_test_basic.py::test_ping": "7945fdf0623a34efada654fe62a57fa06c6ab0cf4383bc14c6b7acd594b10596",
+"T3B1_en_test_basic.py::test_protobuf_recursion": "7945fdf0623a34efada654fe62a57fa06c6ab0cf4383bc14c6b7acd594b10596",
"T3B1_en_test_busy_state.py::test_busy_expiry_core": "c61769cc9da63808d8faf2b78cb694715e0df07ad1f6a05210ca963562b13165",
"T3B1_en_test_busy_state.py::test_busy_state": "820ab7e4542b5723952f6be0eedcdd9f0e83f1c038b0900f4165a2d8c4aeaac9",
"T3B1_en_test_cancel.py::test_cancel_message_via_cancel[message0]": "e9cb3a36efab1cdcad248b99066cd6addb1584af04474c501d99fc1dfaf6ac0d",
@@ -19586,6 +19595,7 @@
"T3B1_es_test_basic.py::test_device_id_same": "1c43a01a3864c17b781e3a68121bb7c4378f858ab5c4db5b3d012f138eba5fb8",
"T3B1_es_test_basic.py::test_not_initialized": "f4521a6e1979e44a1050c326098f98ae88a3a255461919644cb74f6f28369971",
"T3B1_es_test_basic.py::test_ping": "1c43a01a3864c17b781e3a68121bb7c4378f858ab5c4db5b3d012f138eba5fb8",
+"T3B1_es_test_basic.py::test_protobuf_recursion": "1c43a01a3864c17b781e3a68121bb7c4378f858ab5c4db5b3d012f138eba5fb8",
"T3B1_es_test_busy_state.py::test_busy_expiry_core": "3dbc6231add00e6f482d3e4b61a623831e24459b1ebbfd5321728be429fd0e3b",
"T3B1_es_test_busy_state.py::test_busy_state": "165aa87ede957d65717a06b3481737f4cac3576502fd24276e7cf74ef64d53c8",
"T3B1_es_test_cancel.py::test_cancel_message_via_cancel[message0]": "29f0718e269695561d4b0905f0b41f119d7e1d997af431ca6efece6d33de5b49",
@@ -21382,6 +21392,7 @@
"T3B1_fr_test_basic.py::test_device_id_same": "979886e5de665a0a5bd752457a29ad2bd0aad9d94702754cb351dc7d31979b42",
"T3B1_fr_test_basic.py::test_not_initialized": "f4521a6e1979e44a1050c326098f98ae88a3a255461919644cb74f6f28369971",
"T3B1_fr_test_basic.py::test_ping": "979886e5de665a0a5bd752457a29ad2bd0aad9d94702754cb351dc7d31979b42",
+"T3B1_fr_test_basic.py::test_protobuf_recursion": "979886e5de665a0a5bd752457a29ad2bd0aad9d94702754cb351dc7d31979b42",
"T3B1_fr_test_busy_state.py::test_busy_expiry_core": "be8291514dcaead2cf3540777ee19ef4b556c78255f22ee27ebd4ec02c1bdddc",
"T3B1_fr_test_busy_state.py::test_busy_state": "ea4b7ebe8a9215048d8438c998f0edb7e4cb644a545d07bda79db109c8a3ef54",
"T3B1_fr_test_cancel.py::test_cancel_message_via_cancel[message0]": "5f02f4b2d796e946ffa9483f653cf082ab1b94eaed99214fcc165edbfe6f2830",
@@ -23178,6 +23189,7 @@
"T3B1_pt_test_basic.py::test_device_id_same": "bbd866375fe164eb4caaddceb8a49d710b09ad01ca2cf67a0006d3a0d3ecb121",
"T3B1_pt_test_basic.py::test_not_initialized": "f4521a6e1979e44a1050c326098f98ae88a3a255461919644cb74f6f28369971",
"T3B1_pt_test_basic.py::test_ping": "bbd866375fe164eb4caaddceb8a49d710b09ad01ca2cf67a0006d3a0d3ecb121",
+"T3B1_pt_test_basic.py::test_protobuf_recursion": "bbd866375fe164eb4caaddceb8a49d710b09ad01ca2cf67a0006d3a0d3ecb121",
"T3B1_pt_test_busy_state.py::test_busy_expiry_core": "79dc08e6542dd2845bdcd3d81fe090ac565db6ac4c1598972be8b2b4c1b8e958",
"T3B1_pt_test_busy_state.py::test_busy_state": "f6a1247ad3fee509fbedfca83067c5d845e392504744e4da4dcbba682f1d79f9",
"T3B1_pt_test_cancel.py::test_cancel_message_via_cancel[message0]": "66948f196cc9452be012bb023769e446772b15f4ec4b1c9fcb8598ffad1f4ec6",
@@ -25371,6 +25383,7 @@
"T3T1_cs_test_basic.py::test_device_id_same": "9c20243bc5dacf2442af5a8778b66a0ab7cb978e11c02fec371f109fbc9d55f6",
"T3T1_cs_test_basic.py::test_not_initialized": "13fa002861e42e18dbb21ae2376a25cb3738fb4d596814f0dede8a2eb0d36556",
"T3T1_cs_test_basic.py::test_ping": "9c20243bc5dacf2442af5a8778b66a0ab7cb978e11c02fec371f109fbc9d55f6",
+"T3T1_cs_test_basic.py::test_protobuf_recursion": "9c20243bc5dacf2442af5a8778b66a0ab7cb978e11c02fec371f109fbc9d55f6",
"T3T1_cs_test_busy_state.py::test_busy_expiry_core": "b359d206993fccf4f274958a0d98927fd101e4b82cadc2c75b213561899bee6f",
"T3T1_cs_test_busy_state.py::test_busy_state": "c14d1aa01f507ccdb790ae0e169d5e5c268e390899a442cb6ded16473e96c623",
"T3T1_cs_test_cancel.py::test_cancel_message_via_cancel[message0]": "e25fd1353029f2cb7c08bfa0c219b728a79f1b3e099d2ccc152284c0274b96ba",
@@ -27202,6 +27215,7 @@
"T3T1_de_test_basic.py::test_device_id_same": "68c146f13417be0845fee20b1d8de4a05e0844608c2efb3cfae9f573be39f0c4",
"T3T1_de_test_basic.py::test_not_initialized": "53e954ddaaebca92f068e0e13fe87d09798cbd59e85714d25f8f5886956ae58e",
"T3T1_de_test_basic.py::test_ping": "68c146f13417be0845fee20b1d8de4a05e0844608c2efb3cfae9f573be39f0c4",
+"T3T1_de_test_basic.py::test_protobuf_recursion": "68c146f13417be0845fee20b1d8de4a05e0844608c2efb3cfae9f573be39f0c4",
"T3T1_de_test_busy_state.py::test_busy_expiry_core": "081e7f27ad1d2474ab890a7bc27a82559856fd28bf742d724e1f1c60b6c096a3",
"T3T1_de_test_busy_state.py::test_busy_state": "bb62c03d545f485b684def63c64e9342465f7f9a965dd54acb4351a59ab7e52f",
"T3T1_de_test_cancel.py::test_cancel_message_via_cancel[message0]": "7e2753e182efb81afe658d874b4145f9b172a12c8a6542256150535aeb9bd151",
@@ -29033,6 +29047,7 @@
"T3T1_en_test_basic.py::test_device_id_same": "0064e8a52b9bff0c2a31432fa781b800afb21e8910d60f303671bdfc8d8425fc",
"T3T1_en_test_basic.py::test_not_initialized": "4bc037c13dd19f23a176243dfd29921cd51ce9e4155faa6346540a4663233318",
"T3T1_en_test_basic.py::test_ping": "0064e8a52b9bff0c2a31432fa781b800afb21e8910d60f303671bdfc8d8425fc",
+"T3T1_en_test_basic.py::test_protobuf_recursion": "0064e8a52b9bff0c2a31432fa781b800afb21e8910d60f303671bdfc8d8425fc",
"T3T1_en_test_busy_state.py::test_busy_expiry_core": "f71ff591126f9bb2dff350109d376ee6d98b52ef86e36d8e1630f9be400f3d89",
"T3T1_en_test_busy_state.py::test_busy_state": "01165724b80b91682d0970edc98c62e6ecaaf46f8a13516c1c742b85d95845c9",
"T3T1_en_test_cancel.py::test_cancel_message_via_cancel[message0]": "ba3805dd21b3b10137121bd84e4660101a84b4fb81426501e029be5271ca01f4",
@@ -30864,6 +30879,7 @@
"T3T1_es_test_basic.py::test_device_id_same": "41b9c9e85d64891dcfd03d675df77c533f035a2d191b161144d00afab69a589d",
"T3T1_es_test_basic.py::test_not_initialized": "9bb7023f656f4fcd819bd648e96b4a0ea97bb2f04233ce48c72184f363f68c26",
"T3T1_es_test_basic.py::test_ping": "41b9c9e85d64891dcfd03d675df77c533f035a2d191b161144d00afab69a589d",
+"T3T1_es_test_basic.py::test_protobuf_recursion": "41b9c9e85d64891dcfd03d675df77c533f035a2d191b161144d00afab69a589d",
"T3T1_es_test_busy_state.py::test_busy_expiry_core": "376f7335e61e7e8c16f4f59c8668ccb1d494d946704ea3ac51943fa97b13f20f",
"T3T1_es_test_busy_state.py::test_busy_state": "de7c72da43cd455829606e91918e9ab92c24a96720fd41dbf8a98f523d958ac2",
"T3T1_es_test_cancel.py::test_cancel_message_via_cancel[message0]": "408a10213cdc19dddaea9e2c2e09e28737152f6c4c8f0dd70870d0568586f03b",
@@ -32695,6 +32711,7 @@
"T3T1_fr_test_basic.py::test_device_id_same": "116c64aa168d12181b7a5317677ddf135d739fdac22d38ff08571dc3301c2f47",
"T3T1_fr_test_basic.py::test_not_initialized": "67f8a621b6a0468bcc683c0da4278fd8fb644bb29af5b9cee84816691a07e541",
"T3T1_fr_test_basic.py::test_ping": "116c64aa168d12181b7a5317677ddf135d739fdac22d38ff08571dc3301c2f47",
+"T3T1_fr_test_basic.py::test_protobuf_recursion": "116c64aa168d12181b7a5317677ddf135d739fdac22d38ff08571dc3301c2f47",
"T3T1_fr_test_busy_state.py::test_busy_expiry_core": "c7c601028725b56d6e65080fb5c48f79416c3d4ca0ec94b15338eb6555135e7f",
"T3T1_fr_test_busy_state.py::test_busy_state": "6680e9f58e5fed69d85578f1d0a90978553e55282f95f73f081f4c6bc6a1cd7f",
"T3T1_fr_test_cancel.py::test_cancel_message_via_cancel[message0]": "a69f34b0319adccd4d2e0b4b9a69c77772aa8997f0fe46083b1a55b2f9424375",
@@ -34526,6 +34543,7 @@
"T3T1_pt_test_basic.py::test_device_id_same": "14256cd1b801d1b67a37de1198d61a80d342f39e5c5d6fa991ec7fb526d683be",
"T3T1_pt_test_basic.py::test_not_initialized": "0931386d561e5a7edb7f28b4933de40d5474de7078ce741efdbd52da7cbb9128",
"T3T1_pt_test_basic.py::test_ping": "14256cd1b801d1b67a37de1198d61a80d342f39e5c5d6fa991ec7fb526d683be",
+"T3T1_pt_test_basic.py::test_protobuf_recursion": "14256cd1b801d1b67a37de1198d61a80d342f39e5c5d6fa991ec7fb526d683be",
"T3T1_pt_test_busy_state.py::test_busy_expiry_core": "8244bb3b62acd819e1677e61d2e3e719dc0f4b78235b03b089bfdc31d9b533c7",
"T3T1_pt_test_busy_state.py::test_busy_state": "6b6fa4ba46ea4e90e67d7d6f274a68c408bad8d2c89a8ea66c39ddf7af79490a",
"T3T1_pt_test_cancel.py::test_cancel_message_via_cancel[message0]": "a933163fcd7445dc8bd80959ee0ff0466181fb79b51fa45a5f4cfbe72ba252f1",
@@ -37035,6 +37053,7 @@
"T3W1_cs_test_basic.py::test_get_features_avoids_restart": "753bddcb0c7426dab6fb7d8b0459ce4c95e1b47661f538e3771bffffc2f8a6a0",
"T3W1_cs_test_basic.py::test_not_initialized": "e38d7edfe2984c4f024be6d469803c3907a9807a8b33c466fa52e11253f24809",
"T3W1_cs_test_basic.py::test_ping": "2b06a6c36a96faaad9d1237b47e37374c5452331ad8fa3738accbd2e09ec1500",
+"T3W1_cs_test_basic.py::test_protobuf_recursion": "2b06a6c36a96faaad9d1237b47e37374c5452331ad8fa3738accbd2e09ec1500",
"T3W1_cs_test_ble.py::test_ble_unpair_all": "201850f6d92800aaa8ce94b322af7b55c75956f9dc83b6ccb865cf202b3dafab",
"T3W1_cs_test_busy_state.py::test_busy_expiry_core": "ee7e325ec10190b943f7cc7ed086f5078b187b79376488b954f9e49badc633a1",
"T3W1_cs_test_busy_state.py::test_busy_state": "03804917a8113787c0111cd50ce62e17872170b03cce9db843c2a9701907ca5a",
@@ -38901,6 +38920,7 @@
"T3W1_de_test_basic.py::test_get_features_avoids_restart": "7227699851381681ee306433b4bd7be983538a38fc98cf3cc402ac5c46a8c261",
"T3W1_de_test_basic.py::test_not_initialized": "e38d7edfe2984c4f024be6d469803c3907a9807a8b33c466fa52e11253f24809",
"T3W1_de_test_basic.py::test_ping": "32c06c9f4ef6d91d183e5480b273a0aec9bd56572528ddbfb70b7cd609cbef50",
+"T3W1_de_test_basic.py::test_protobuf_recursion": "32c06c9f4ef6d91d183e5480b273a0aec9bd56572528ddbfb70b7cd609cbef50",
"T3W1_de_test_ble.py::test_ble_unpair_all": "3f156ca355d73e95ae54abb628ba6b423f1ec05571796a1e2dfaed515b4b6d2f",
"T3W1_de_test_busy_state.py::test_busy_expiry_core": "2311bd09fce29a0282e24f0d4cf0d981ea2ef121149618594a9262eba9e9160f",
"T3W1_de_test_busy_state.py::test_busy_state": "4d6bbe8c60ac095fc8e5fe3a2e4260ac2319cd99a03a4097aa0e0fa70fc333b2",
@@ -40767,6 +40787,7 @@
"T3W1_en_test_basic.py::test_get_features_avoids_restart": "b32601d8ffc9333e411bd041c1e98acff5dfe66b96d3129c6299e9dcc6e6d46b",
"T3W1_en_test_basic.py::test_not_initialized": "e38d7edfe2984c4f024be6d469803c3907a9807a8b33c466fa52e11253f24809",
"T3W1_en_test_basic.py::test_ping": "90812f196e8c35dc7f35cb5a9a0e553c8fc6a610812d85b9f166e90b3f1c8b59",
+"T3W1_en_test_basic.py::test_protobuf_recursion": "90812f196e8c35dc7f35cb5a9a0e553c8fc6a610812d85b9f166e90b3f1c8b59",
"T3W1_en_test_ble.py::test_ble_unpair_all": "115ab7548d470609316dc95d1550ac70ab985f3ecf58738ed25bb5ec42758709",
"T3W1_en_test_busy_state.py::test_busy_expiry_core": "94690c9b5b15f3c905410a963bb2dbfbac84127ea088c37389de086db2c181da",
"T3W1_en_test_busy_state.py::test_busy_state": "ca612db4970230f18bcd28e62b32971acb0a65f4be87c0e8addb775b9ce7726a",
@@ -42633,6 +42654,7 @@
"T3W1_es_test_basic.py::test_get_features_avoids_restart": "61600fb93e15ce9a831486d42eda9f24613f80921f0c46eb73c46855c84a3833",
"T3W1_es_test_basic.py::test_not_initialized": "e38d7edfe2984c4f024be6d469803c3907a9807a8b33c466fa52e11253f24809",
"T3W1_es_test_basic.py::test_ping": "ddbd1412a9f69aea1c41c375a2ec0647411a0e487ec5c1ee55e24505dfc34e29",
+"T3W1_es_test_basic.py::test_protobuf_recursion": "ddbd1412a9f69aea1c41c375a2ec0647411a0e487ec5c1ee55e24505dfc34e29",
"T3W1_es_test_ble.py::test_ble_unpair_all": "b0d15c7fcf16f187765bc61dc44243d140d5f3bc7f8e955236f6dd254e9c765d",
"T3W1_es_test_busy_state.py::test_busy_expiry_core": "a3548ef3c5e0f9d933f86e09a21280f396bfb58bf7949bc60ad6d0703083a469",
"T3W1_es_test_busy_state.py::test_busy_state": "0fe51f4a5dcdc3a6275b34e2ca3984c41e909c97132e1bb447651e75c6474743",
@@ -44499,6 +44521,7 @@
"T3W1_fr_test_basic.py::test_get_features_avoids_restart": "cd0fd4072f66e9b5151201c0a21fea686406d3d9167a4632ef7d75c3cfb2a7b0",
"T3W1_fr_test_basic.py::test_not_initialized": "e38d7edfe2984c4f024be6d469803c3907a9807a8b33c466fa52e11253f24809",
"T3W1_fr_test_basic.py::test_ping": "a392ca1dea666647fb2d6edfcd90e4e3cad6ab0e294ca85cda7db478214a6a81",
+"T3W1_fr_test_basic.py::test_protobuf_recursion": "a392ca1dea666647fb2d6edfcd90e4e3cad6ab0e294ca85cda7db478214a6a81",
"T3W1_fr_test_ble.py::test_ble_unpair_all": "4ec93bba7bc8688f84f65d64de5acbf3f7081e55796bb9de1c62487f1b6853f2",
"T3W1_fr_test_busy_state.py::test_busy_expiry_core": "41e4bbbb9f9e2249ccee5064fd902daf1c9475efc9c4040b8bb9a4feff8e0022",
"T3W1_fr_test_busy_state.py::test_busy_state": "d2569e8e45a117a729b765174ebc759184e391544d45320a818ee6e459d4fb6d",
@@ -46370,6 +46393,7 @@
"T3W1_pt_test_basic.py::test_get_features_avoids_restart": "2afba62a11dc2554b37fd62befd153df287732c897c2fe68df740ef36ecfd0b5",
"T3W1_pt_test_basic.py::test_not_initialized": "e38d7edfe2984c4f024be6d469803c3907a9807a8b33c466fa52e11253f24809",
"T3W1_pt_test_basic.py::test_ping": "6fb22dca4b252028ed3f91db1fb746e30d12aece0a74e1bcc4abef605a3a9026",
+"T3W1_pt_test_basic.py::test_protobuf_recursion": "6fb22dca4b252028ed3f91db1fb746e30d12aece0a74e1bcc4abef605a3a9026",
"T3W1_pt_test_ble.py::test_ble_unpair_all": "5af6269b8cc4998d3bf05945c5f893cb7bd349040e466fae7ff193c977931120",
"T3W1_pt_test_busy_state.py::test_busy_expiry_core": "7805e03d90472b862ede9a0c716ff2c86e7aabedbc514af84a38ece46f9f392b",
"T3W1_pt_test_busy_state.py::test_busy_state": "38f0c6c5fe3bec77d34c66542acfdf0a0668cb02be01c19e9e45ef5c49b0d35b",Why this scored 66/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.