refactor(core): add option that prints basic emulator properties
What changed, and why it matters
This commit adds a new command-line option to the Trezor emulator that prints basic device information (model name, optional feature flags, and version number) as JSON and then exits. It also adds a Python helper to call this option. There is no security-relevant change: it only exposes non-sensitive metadata that is already known or easily discoverable, and it does not alter cryptographic, storage, or communication behavior.
No security action required. This is a benign developer/testing feature. If desired, verify that the printed version/model strings are not considered sensitive in any specific deployment context, but they are standard public metadata.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch introduces --emulator-properties in core/embed/projects/unix/main.c usage text and implements it in main_main.c by checking argv[1] and calling a new print_emulator_properties_and_exit() function. That function prints MODEL_INTERNAL_NAME, conditional USE_TROPIC/USE_BLE flags, and VERSION_MAJOR/MINOR/PATCH/BUILD as JSON, then exits. python/src/trezorlib/_internal/emulator.py adds a properties() method on the base Emulator class (returns {}) and overrides it in CoreEmulator to invoke the binary with the new flag, parse the JSON, and return the dict. The feature is intended for test infrastructure to identify emulator capabilities before starting it, including before UDP sockets are bound.
Changed components
core/embed/projects/unix/main.ccore/embed/projects/unix/main_main.cpython/src/trezorlib/_internal/emulator.pyInspect captured patch +49 / −0
diff --git a/core/embed/projects/unix/main.c b/core/embed/projects/unix/main.c
index 5a3bd2ea..fabd3ae8 100644
--- a/core/embed/projects/unix/main.c
+++ b/core/embed/projects/unix/main.c
@@ -308,6 +308,7 @@ STATIC int usage(char **argv) {
printf(
"usage: %s [<opts>] [-X <implopt>] [-c <command>] [<filename>]\n"
"Options:\n"
+ "--emulator-properties : print basic emulator info and exit\n"
"-v : verbose (trace various operations); can be multiple\n"
"-O[N] : apply bytecode optimizations of level N\n"
"\n"
diff --git a/core/embed/projects/unix/main_main.c b/core/embed/projects/unix/main_main.c
index 7b766d9f..038951f3 100644
--- a/core/embed/projects/unix/main_main.c
+++ b/core/embed/projects/unix/main_main.c
@@ -79,6 +79,8 @@
#include <SDL3/SDL.h>
+#include "version.h"
+
static void drivers_deinit(void) { flash_deinit(); }
static void drivers_init(void) {
@@ -169,6 +171,22 @@ static bool sdl_event_filter(void *userdata, SDL_Event *event) {
return true;
}
+// Can be used to get basic emulator properties before regularly starting it.
+// Needs to be called before it starts listening on UDP sockets in case there
+// are multiple copies of the process called, e.g. from multicore tests.
+static void print_emulator_properties_and_exit(void) {
+ printf("{\"internal_model\": \"%s\",\n", MODEL_INTERNAL_NAME);
+#ifdef USE_TROPIC
+ printf(" \"tropic\": true,\n");
+#endif
+#ifdef USE_BLE
+ printf(" \"ble\": true,\n");
+#endif
+ printf(" \"version\": \"%d.%d.%d.%d\"}\n", VERSION_MAJOR, VERSION_MINOR,
+ VERSION_PATCH, VERSION_BUILD);
+ exit(0);
+}
+
// Kernel task main loop
//
// Returns when the coreapp task is terminated
@@ -185,6 +203,10 @@ static void kernel_loop(applet_t *coreapp) {
int main(int argc, char **argv) {
system_init(&rsod_panic_handler);
+ if (argc > 1 && strcmp(argv[1], "--emulator-properties") == 0) {
+ print_emulator_properties_and_exit();
+ }
+
#ifdef USE_MCU_ATTESTATION
{
uint8_t mcu_device_cert[MCU_ATTESTATION_MAX_CERT_SIZE];
diff --git a/python/src/trezorlib/_internal/emulator.py b/python/src/trezorlib/_internal/emulator.py
index d3577900..18e94ff2 100644
--- a/python/src/trezorlib/_internal/emulator.py
+++ b/python/src/trezorlib/_internal/emulator.py
@@ -17,6 +17,7 @@
from __future__ import annotations
import atexit
+import json
import logging
import os
import signal
@@ -355,6 +356,9 @@ class Emulator:
def get_storage(self) -> bytes:
return self.storage.read_bytes()
+ def properties(self) -> dict:
+ return {}
+
class CoreEmulator(Emulator):
STORAGE_FILENAME = "trezor.flash"
@@ -429,6 +433,28 @@ class CoreEmulator(Emulator):
def tropic_port(self) -> int:
return self.tropic_model_port or (self.port + 6)
+ def properties(self) -> dict[str, Any]:
+ args = [str(self.executable), "--emulator-properties"]
+ try:
+ stdout = subprocess.check_output(
+ args,
+ cwd=self.workdir,
+ env=self.make_env(),
+ stderr=subprocess.PIPE,
+ text=True,
+ timeout=5,
+ )
+ props = json.loads(stdout)
+ assert isinstance(props, dict)
+ return props
+ except subprocess.CalledProcessError as exc:
+ LOG.warning(
+ f"{' '.join(args)} failed: {exc}\nstdout: {exc.stdout}\nstderr: {exc.stderr}"
+ )
+ except Exception as exc:
+ LOG.warning(f"{' '.join(args)} failed: {exc}")
+ return {}
+
class LegacyEmulator(Emulator):
STORAGE_FILENAME = "emulator.img"
Why this scored 15/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.