feat(core/eckhart): cache THP app/host name along with MAC
What changed, and why it matters
This commit is a small feature update for the Trezor hardware wallet's Bluetooth pairing system. It adds a new 'app_name' field to the cached information stored alongside each paired device's MAC address and host name. The change is purely additive and does not appear to fix or introduce any security vulnerability. It updates protocol definitions, device menu display helpers, pairing logic, and generated code in multiple languages, plus related tests.
No security action required. Treat as normal feature commit. If reviewing for release readiness, verify that the new required protobuf field does not break backward compatibility with older host software that may not send app_name during pairing.
Security signals we found
No security-relevant keywords in commit title or message
No changelog entry, consistent with routine feature work
Additive schema change only; no validation, crypto, or authorization logic modified
No input parsing changes beyond existing trim_str length limit
No references to CVEs, advisories, or security reports
Evidence from the diff
The commit extends the THP (Trezor Host Protocol) paired-device cache schema by adding a required string field app_name to ThpPairedCacheEntry. It updates the protobuf definition, generated Python and Rust message classes, the firmware’s pairing cache helper, and the device-menu UI helper that looks up host names. The new field is trimmed to 32 bytes and stored with the existing mac_addr and host_name. Test expectations for serialized cache size are adjusted accordingly. No security fixes or vulnerability indicators are present in the diff.
Changed components
common/protob/messages-thp.protocore/src/apps/homescreen/device_menu.pycore/src/apps/thp/pairing.pycore/src/trezor/messages.pycore/tests/test_apps.thp.paired_cache.pypython/src/trezorlib/messages.pyrust/trezor-client/src/protos/generated/messages_thp.rsInspect captured patch +149 / −49
diff --git a/common/protob/messages-thp.proto b/common/protob/messages-thp.proto
index 3e366f60c..03a7a40ea 100644
--- a/common/protob/messages-thp.proto
+++ b/common/protob/messages-thp.proto
@@ -288,5 +288,6 @@ message ThpPairedCache {
option (internal_only) = true;
required bytes mac_addr = 1; // 6-byte MAC address
required string host_name = 2; // Human-readable host name (≤32 bytes)
+ required string app_name = 3; // Human-readable application name (≤32 bytes)
}
}
diff --git a/core/src/apps/homescreen/device_menu.py b/core/src/apps/homescreen/device_menu.py
index d77829a22..65add15ee 100644
--- a/core/src/apps/homescreen/device_menu.py
+++ b/core/src/apps/homescreen/device_menu.py
@@ -1,4 +1,5 @@
from micropython import const
+from typing import TYPE_CHECKING
import storage.device as storage_device
import trezorble as ble
@@ -8,6 +9,9 @@ from trezor.ui.layouts import interact, raise_if_cancelled
from trezor.wire import ActionCancelled, PinCancelled
from trezorui_api import CANCELLED, DeviceMenuResult
+if TYPE_CHECKING:
+ from trezor.messages import ThpPairedCacheEntry
+
BLE_MAX_BONDS = 8
@@ -24,11 +28,21 @@ class SubmenuId:
POWER = const(8)
-def _get_hostname(ble_addr: bytes, hostname_map: dict[bytes, str]) -> str:
- if (hostname := hostname_map.get(ble_addr)) is None:
- # Internal MAC address representation is using reversed byte order.
- return ":".join(f"{byte:02X}" for byte in reversed(ble_addr))
- return hostname
+def _get_hostinfo(
+ ble_addr: bytes, hostname_map: dict[bytes, ThpPairedCacheEntry]
+) -> tuple[str, tuple[str, str] | None]:
+ # Internal MAC address representation is using reversed byte order.
+ mac = ":".join(f"{byte:02X}" for byte in reversed(ble_addr))
+ if hostinfo := hostname_map.get(ble_addr):
+ return (mac, (hostinfo.host_name, hostinfo.app_name))
+ return (mac, None)
+
+
+def _get_hostname(
+ ble_addr: bytes, hostname_map: dict[bytes, ThpPairedCacheEntry]
+) -> str:
+ mac, hostinfo = _get_hostinfo(ble_addr, hostname_map)
+ return mac if hostinfo is None else hostinfo[0]
def _find_device(connected_addr: bytes | None, bonds: list[bytes]) -> int | None:
@@ -77,7 +91,7 @@ async def handle_device_menu() -> None:
connected_idx = _find_device(connected_addr, bonds)
if __debug__:
log.debug(__name__, "connected: %s (%s)", connected_addr, connected_idx)
- hostname_map = {e.mac_addr: e.host_name for e in paired_cache.load()}
+ hostname_map = {e.mac_addr: e for e in paired_cache.load()}
if __debug__:
log.debug(__name__, "hostname_map: %s", hostname_map)
paired_devices = [_get_hostname(bond, hostname_map) for bond in bonds]
diff --git a/core/src/apps/thp/pairing.py b/core/src/apps/thp/pairing.py
index 44213c445..e8cc9b46d 100644
--- a/core/src/apps/thp/pairing.py
+++ b/core/src/apps/thp/pairing.py
@@ -121,7 +121,7 @@ async def handle_pairing_request(
ctx.host_name = message.host_name
ctx.app_name = message.app_name
if peer_addr is not None:
- _cache_host_name(peer_addr, ctx.host_name)
+ _cache_host_info(peer_addr, ctx.host_name, ctx.app_name)
await ctx.write(ThpPairingRequestApproved())
assert ThpSelectMethod.MESSAGE_WIRE_TYPE is not None
@@ -481,7 +481,7 @@ def _check_method_is_selected(ctx: PairingContext, method: ThpPairingMethod) ->
raise ThpError("Not selected pairing method")
-def _cache_host_name(mac_addr: bytes, host_name: str) -> None:
+def _cache_host_info(mac_addr: bytes, host_name: str, app_name: str) -> None:
from trezor.messages import ThpPairedCacheEntry
from trezor.strings import trim_str
@@ -493,5 +493,8 @@ def _cache_host_name(mac_addr: bytes, host_name: str) -> None:
return
host_name = trim_str(host_name, max_bytes=32)
- entries.append(ThpPairedCacheEntry(mac_addr=mac_addr, host_name=host_name))
+ app_name = trim_str(app_name, max_bytes=32)
+ entries.append(
+ ThpPairedCacheEntry(mac_addr=mac_addr, host_name=host_name, app_name=app_name)
+ )
paired_cache.store(entries)
diff --git a/core/src/trezor/messages.py b/core/src/trezor/messages.py
index cbabaa3b3..b51072052 100644
--- a/core/src/trezor/messages.py
+++ b/core/src/trezor/messages.py
@@ -6659,12 +6659,14 @@ if TYPE_CHECKING:
class ThpPairedCacheEntry(protobuf.MessageType):
mac_addr: "bytes"
host_name: "str"
+ app_name: "str"
def __init__(
self,
*,
mac_addr: "bytes",
host_name: "str",
+ app_name: "str",
) -> None:
pass
diff --git a/core/tests/test_apps.thp.paired_cache.py b/core/tests/test_apps.thp.paired_cache.py
index a9fcc368e..5d4ec0f5f 100644
--- a/core/tests/test_apps.thp.paired_cache.py
+++ b/core/tests/test_apps.thp.paired_cache.py
@@ -9,14 +9,30 @@ if utils.USE_THP:
from apps.thp import paired_cache
ALL_ENTRIES = [
- ThpPairedCacheEntry(mac_addr=b"\x01\x02\x03\x04\x05\x06", host_name="First"),
- ThpPairedCacheEntry(mac_addr=b"\x11\x12\x13\x14\x15\x16", host_name="Second"),
- ThpPairedCacheEntry(mac_addr=b"\x21\x22\x23\x24\x25\x26", host_name="Third"),
- ThpPairedCacheEntry(mac_addr=b"\x31\x32\x33\x34\x35\x36", host_name="Fourth"),
- ThpPairedCacheEntry(mac_addr=b"\x41\x42\x43\x44\x45\x46", host_name="Fifth"),
- ThpPairedCacheEntry(mac_addr=b"\x51\x52\x53\x54\x55\x56", host_name="Sixth"),
- ThpPairedCacheEntry(mac_addr=b"\x61\x62\x63\x64\x65\x66", host_name="Seventh"),
- ThpPairedCacheEntry(mac_addr=b"\x71\x72\x73\x74\x75\x76", host_name="Eighth"),
+ ThpPairedCacheEntry(
+ mac_addr=b"\x01\x02\x03\x04\x05\x06", host_name="First", app_name="App1"
+ ),
+ ThpPairedCacheEntry(
+ mac_addr=b"\x11\x12\x13\x14\x15\x16", host_name="Second", app_name="App2"
+ ),
+ ThpPairedCacheEntry(
+ mac_addr=b"\x21\x22\x23\x24\x25\x26", host_name="Third", app_name="App3"
+ ),
+ ThpPairedCacheEntry(
+ mac_addr=b"\x31\x32\x33\x34\x35\x36", host_name="Fourth", app_name="App4"
+ ),
+ ThpPairedCacheEntry(
+ mac_addr=b"\x41\x42\x43\x44\x45\x46", host_name="Fifth", app_name="App5"
+ ),
+ ThpPairedCacheEntry(
+ mac_addr=b"\x51\x52\x53\x54\x55\x56", host_name="Sixth", app_name="App6"
+ ),
+ ThpPairedCacheEntry(
+ mac_addr=b"\x61\x62\x63\x64\x65\x66", host_name="Seventh", app_name="App7"
+ ),
+ ThpPairedCacheEntry(
+ mac_addr=b"\x71\x72\x73\x74\x75\x76", host_name="Eighth", app_name="App8"
+ ),
]
@@ -74,9 +90,11 @@ class TestTrezorHostProtocolPairedCache(unittest.TestCase):
def test_max_size(self):
self.assertIsNone(get_thp_paired_cache())
- # serialize longest `host_name`` and maximal number of bonds
+ # serialize longest `host_name` and `app_name` and maximal number of bonds
entries = [
- ThpPairedCacheEntry(mac_addr=bytes([i] * 6), host_name=f"{i}" * 32)
+ ThpPairedCacheEntry(
+ mac_addr=bytes([i] * 6), host_name=f"{i}" * 32, app_name=f"{i}" * 32
+ )
for i in range(8)
]
bonds = {e.mac_addr for e in entries}
@@ -86,8 +104,9 @@ class TestTrezorHostProtocolPairedCache(unittest.TestCase):
cache_blob = get_thp_paired_cache()
self.assertIsNotNone(cache_blob)
# Check that serialized size is not too large:
- # 8 entries x (32 bytes [name] + 6 bytes [addr]) = 304 bytes
- assert len(cache_blob) <= 400
+ # 8 entries x (32 bytes [host name] + 32 bytes [app name] + 6 bytes [addr]) = 560 bytes
+ # plus some protobuf overhead
+ assert len(cache_blob) <= 624
if __name__ == "__main__":
diff --git a/python/src/trezorlib/messages.py b/python/src/trezorlib/messages.py
index da336e75a..d9cf684c9 100644
--- a/python/src/trezorlib/messages.py
+++ b/python/src/trezorlib/messages.py
@@ -8424,6 +8424,7 @@ class ThpPairedCacheEntry(protobuf.MessageType):
FIELDS = {
1: protobuf.Field("mac_addr", "bytes", repeated=False, required=True),
2: protobuf.Field("host_name", "string", repeated=False, required=True),
+ 3: protobuf.Field("app_name", "string", repeated=False, required=True),
}
def __init__(
@@ -8431,9 +8432,11 @@ class ThpPairedCacheEntry(protobuf.MessageType):
*,
mac_addr: "bytes",
host_name: "str",
+ app_name: "str",
) -> None:
self.mac_addr = mac_addr
self.host_name = host_name
+ self.app_name = app_name
class WebAuthnListResidentCredentials(protobuf.MessageType):
diff --git a/rust/trezor-client/src/protos/generated/messages_thp.rs b/rust/trezor-client/src/protos/generated/messages_thp.rs
index 144d4aca9..5985cf89f 100644
--- a/rust/trezor-client/src/protos/generated/messages_thp.rs
+++ b/rust/trezor-client/src/protos/generated/messages_thp.rs
@@ -4229,6 +4229,8 @@ pub mod thp_paired_cache {
pub mac_addr: ::std::option::Option<::std::vec::Vec<u8>>,
// @@protoc_insertion_point(field:hw.trezor.messages.thp.ThpPairedCache.ThpPairedCacheEntry.host_name)
pub host_name: ::std::option::Option<::std::string::String>,
+ // @@protoc_insertion_point(field:hw.trezor.messages.thp.ThpPairedCache.ThpPairedCacheEntry.app_name)
+ pub app_name: ::std::option::Option<::std::string::String>,
// special fields
// @@protoc_insertion_point(special_field:hw.trezor.messages.thp.ThpPairedCache.ThpPairedCacheEntry.special_fields)
pub special_fields: ::protobuf::SpecialFields,
@@ -4317,8 +4319,44 @@ pub mod thp_paired_cache {
self.host_name.take().unwrap_or_else(|| ::std::string::String::new())
}
+ // required string app_name = 3;
+
+ pub fn app_name(&self) -> &str {
+ match self.app_name.as_ref() {
+ Some(v) => v,
+ None => "",
+ }
+ }
+
+ pub fn clear_app_name(&mut self) {
+ self.app_name = ::std::option::Option::None;
+ }
+
+ pub fn has_app_name(&self) -> bool {
+ self.app_name.is_some()
+ }
+
+ // Param is passed by value, moved
+ pub fn set_app_name(&mut self, v: ::std::string::String) {
+ self.app_name = ::std::option::Option::Some(v);
+ }
+
+ // Mutable pointer to the field.
+ // If field is not initialized, it is initialized with default value first.
+ pub fn mut_app_name(&mut self) -> &mut ::std::string::String {
+ if self.app_name.is_none() {
+ self.app_name = ::std::option::Option::Some(::std::string::String::new());
+ }
+ self.app_name.as_mut().unwrap()
+ }
+
+ // Take field
+ pub fn take_app_name(&mut self) -> ::std::string::String {
+ self.app_name.take().unwrap_or_else(|| ::std::string::String::new())
+ }
+
pub(in super) fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
- let mut fields = ::std::vec::Vec::with_capacity(2);
+ let mut fields = ::std::vec::Vec::with_capacity(3);
let mut oneofs = ::std::vec::Vec::with_capacity(0);
fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
"mac_addr",
@@ -4330,6 +4368,11 @@ pub mod thp_paired_cache {
|m: &ThpPairedCacheEntry| { &m.host_name },
|m: &mut ThpPairedCacheEntry| { &mut m.host_name },
));
+ fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
+ "app_name",
+ |m: &ThpPairedCacheEntry| { &m.app_name },
+ |m: &mut ThpPairedCacheEntry| { &mut m.app_name },
+ ));
::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<ThpPairedCacheEntry>(
"ThpPairedCache.ThpPairedCacheEntry",
fields,
@@ -4348,6 +4391,9 @@ pub mod thp_paired_cache {
if self.host_name.is_none() {
return false;
}
+ if self.app_name.is_none() {
+ return false;
+ }
true
}
@@ -4360,6 +4406,9 @@ pub mod thp_paired_cache {
18 => {
self.host_name = ::std::option::Option::Some(is.read_string()?);
},
+ 26 => {
+ self.app_name = ::std::option::Option::Some(is.read_string()?);
+ },
tag => {
::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
},
@@ -4378,6 +4427,9 @@ pub mod thp_paired_cache {
if let Some(v) = self.host_name.as_ref() {
my_size += ::protobuf::rt::string_size(2, &v);
}
+ if let Some(v) = self.app_name.as_ref() {
+ my_size += ::protobuf::rt::string_size(3, &v);
+ }
my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
self.special_fields.cached_size().set(my_size as u32);
my_size
@@ -4390,6 +4442,9 @@ pub mod thp_paired_cache {
if let Some(v) = self.host_name.as_ref() {
os.write_string(2, v)?;
}
+ if let Some(v) = self.app_name.as_ref() {
+ os.write_string(3, v)?;
+ }
os.write_unknown_fields(self.special_fields.unknown_fields())?;
::std::result::Result::Ok(())
}
@@ -4409,6 +4464,7 @@ pub mod thp_paired_cache {
fn clear(&mut self) {
self.mac_addr = ::std::option::Option::None;
self.host_name = ::std::option::Option::None;
+ self.app_name = ::std::option::Option::None;
self.special_fields.clear();
}
@@ -4416,6 +4472,7 @@ pub mod thp_paired_cache {
static instance: ThpPairedCacheEntry = ThpPairedCacheEntry {
mac_addr: ::std::option::Option::None,
host_name: ::std::option::Option::None,
+ app_name: ::std::option::Option::None,
special_fields: ::protobuf::SpecialFields::new(),
};
&instance
@@ -4737,34 +4794,35 @@ static file_descriptor_proto_data: &'static [u8] = b"\
ialData\x123\n\x16host_static_public_key\x18\x01\x20\x02(\x0cR\x13hostSt\
aticPublicKey\x12R\n\rcred_metadata\x18\x02\x20\x02(\x0b2-.hw.trezor.mes\
sages.thp.ThpCredentialMetadataR\x0ccredMetadata:\x04\x98\xb2\x19\x01\"\
- \xc1\x01\n\x0eThpPairedCache\x12T\n\x07entries\x18\x01\x20\x03(\x0b2:.hw\
- .trezor.messages.thp.ThpPairedCache.ThpPairedCacheEntryR\x07entries\x1aS\
+ \xdc\x01\n\x0eThpPairedCache\x12T\n\x07entries\x18\x01\x20\x03(\x0b2:.hw\
+ .trezor.messages.thp.ThpPairedCache.ThpPairedCacheEntryR\x07entries\x1an\
\n\x13ThpPairedCacheEntry\x12\x19\n\x08mac_addr\x18\x01\x20\x02(\x0cR\
- \x07macAddr\x12\x1b\n\thost_name\x18\x02\x20\x02(\tR\x08hostName:\x04\
- \x98\xb2\x19\x01:\x04\x98\xb2\x19\x01*\xfb\x06\n\x0eThpMessageType\x12\
- \x19\n\x15ThpMessageType_Cancel\x10\x14\x12\x20\n\x1cThpMessageType_Butt\
- onRequest\x10\x1a\x12\x1c\n\x18ThpMessageType_ButtonAck\x10\x1b\x12%\n\
- \x20ThpMessageType_ThpPairingRequest\x10\xf0\x07\x12-\n(ThpMessageType_T\
- hpPairingRequestApproved\x10\xf1\x07\x12#\n\x1eThpMessageType_ThpSelectM\
- ethod\x10\xf2\x07\x122\n-ThpMessageType_ThpPairingPreparationsFinished\
- \x10\xf3\x07\x12(\n#ThpMessageType_ThpCredentialRequest\x10\xf8\x07\x12)\
- \n$ThpMessageType_ThpCredentialResponse\x10\xf9\x07\x12!\n\x1cThpMessage\
- Type_ThpEndRequest\x10\xfa\x07\x12\"\n\x1dThpMessageType_ThpEndResponse\
- \x10\xfb\x07\x12*\n%ThpMessageType_ThpCodeEntryCommitment\x10\x80\x08\
- \x12)\n$ThpMessageType_ThpCodeEntryChallenge\x10\x81\x08\x12+\n&ThpMessa\
- geType_ThpCodeEntryCpaceTrezor\x10\x82\x08\x12,\n'ThpMessageType_ThpCode\
- EntryCpaceHostTag\x10\x83\x08\x12&\n!ThpMessageType_ThpCodeEntrySecret\
- \x10\x84\x08\x12\x20\n\x1bThpMessageType_ThpQrCodeTag\x10\x88\x08\x12#\n\
- \x1eThpMessageType_ThpQrCodeSecret\x10\x89\x08\x12!\n\x1cThpMessageType_\
- ThpNfcTagHost\x10\x90\x08\x12#\n\x1eThpMessageType_ThpNfcTagTrezor\x10\
- \x91\x08\x1a\x04\xd0\xf3\x18\x01\"\x04\x08\0\x10\x13\"\x04\x08\x15\x10\
- \x19\"\x05\x08\x1c\x10\xe7\x07\"\x06\x08\xe8\x07\x10\xe8\x07\"\x06\x08\
- \xe9\x07\x10\xef\x07\"\x06\x08\xf4\x07\x10\xf7\x07\"\x06\x08\xfc\x07\x10\
- \xff\x07\"\x06\x08\x85\x08\x10\x87\x08\"\x06\x08\x8a\x08\x10\x8f\x08\"\
- \x06\x08\x92\x08\x10\xcb\x08\"\t\x08\xcc\x08\x10\xff\xff\xff\xff\x07*G\n\
- \x10ThpPairingMethod\x12\x0f\n\x0bSkipPairing\x10\x01\x12\r\n\tCodeEntry\
- \x10\x02\x12\n\n\x06QrCode\x10\x03\x12\x07\n\x03NFC\x10\x04B;\n#com.sato\
- shilabs.trezor.lib.protobufB\x10TrezorMessageThp\x80\xa6\x1d\x01\
+ \x07macAddr\x12\x1b\n\thost_name\x18\x02\x20\x02(\tR\x08hostName\x12\x19\
+ \n\x08app_name\x18\x03\x20\x02(\tR\x07appName:\x04\x98\xb2\x19\x01:\x04\
+ \x98\xb2\x19\x01*\xfb\x06\n\x0eThpMessageType\x12\x19\n\x15ThpMessageTyp\
+ e_Cancel\x10\x14\x12\x20\n\x1cThpMessageType_ButtonRequest\x10\x1a\x12\
+ \x1c\n\x18ThpMessageType_ButtonAck\x10\x1b\x12%\n\x20ThpMessageType_ThpP\
+ airingRequest\x10\xf0\x07\x12-\n(ThpMessageType_ThpPairingRequestApprove\
+ d\x10\xf1\x07\x12#\n\x1eThpMessageType_ThpSelectMethod\x10\xf2\x07\x122\
+ \n-ThpMessageType_ThpPairingPreparationsFinished\x10\xf3\x07\x12(\n#ThpM\
+ essageType_ThpCredentialRequest\x10\xf8\x07\x12)\n$ThpMessageType_ThpCre\
+ dentialResponse\x10\xf9\x07\x12!\n\x1cThpMessageType_ThpEndRequest\x10\
+ \xfa\x07\x12\"\n\x1dThpMessageType_ThpEndResponse\x10\xfb\x07\x12*\n%Thp\
+ MessageType_ThpCodeEntryCommitment\x10\x80\x08\x12)\n$ThpMessageType_Thp\
+ CodeEntryChallenge\x10\x81\x08\x12+\n&ThpMessageType_ThpCodeEntryCpaceTr\
+ ezor\x10\x82\x08\x12,\n'ThpMessageType_ThpCodeEntryCpaceHostTag\x10\x83\
+ \x08\x12&\n!ThpMessageType_ThpCodeEntrySecret\x10\x84\x08\x12\x20\n\x1bT\
+ hpMessageType_ThpQrCodeTag\x10\x88\x08\x12#\n\x1eThpMessageType_ThpQrCod\
+ eSecret\x10\x89\x08\x12!\n\x1cThpMessageType_ThpNfcTagHost\x10\x90\x08\
+ \x12#\n\x1eThpMessageType_ThpNfcTagTrezor\x10\x91\x08\x1a\x04\xd0\xf3\
+ \x18\x01\"\x04\x08\0\x10\x13\"\x04\x08\x15\x10\x19\"\x05\x08\x1c\x10\xe7\
+ \x07\"\x06\x08\xe8\x07\x10\xe8\x07\"\x06\x08\xe9\x07\x10\xef\x07\"\x06\
+ \x08\xf4\x07\x10\xf7\x07\"\x06\x08\xfc\x07\x10\xff\x07\"\x06\x08\x85\x08\
+ \x10\x87\x08\"\x06\x08\x8a\x08\x10\x8f\x08\"\x06\x08\x92\x08\x10\xcb\x08\
+ \"\t\x08\xcc\x08\x10\xff\xff\xff\xff\x07*G\n\x10ThpPairingMethod\x12\x0f\
+ \n\x0bSkipPairing\x10\x01\x12\r\n\tCodeEntry\x10\x02\x12\n\n\x06QrCode\
+ \x10\x03\x12\x07\n\x03NFC\x10\x04B;\n#com.satoshilabs.trezor.lib.protobu\
+ fB\x10TrezorMessageThp\x80\xa6\x1d\x01\
";
/// `FileDescriptorProto` object which was a source for this generated file
Why this scored 19/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.