diff --git a/app/Makefile.version b/app/Makefile.version index fe1028b9..c2990b27 100644 --- a/app/Makefile.version +++ b/app/Makefile.version @@ -1,3 +1,3 @@ APPVERSION_M=2 APPVERSION_N=8 -APPVERSION_P=2 +APPVERSION_P=3 diff --git a/app/rust/src/lib.rs b/app/rust/src/lib.rs index 8bd3a5a2..304bd4e2 100644 --- a/app/rust/src/lib.rs +++ b/app/rust/src/lib.rs @@ -102,6 +102,20 @@ pub extern "C" fn sign_sr25519_phase1( ) { c_zemu_log_stack(b"sign_sr25519\x00".as_ref()); + // Null guards at the FFI boundary. The current in-repo C callers all + // pass valid stack buffers, but passing NULL to `from_raw_parts` is UB + // regardless of whether the slice is later read. Silent early-return + // keeps the `void` signature unchanged; callers that pass NULL get a + // no-op and an unchanged signature buffer. + if sk_ristretto_expanded_ptr.is_null() + || pk_ptr.is_null() + || context_ptr.is_null() + || msg_ptr.is_null() + || sig_ptr.is_null() + { + return; + } + let sk_ristretto_expanded = unsafe { from_raw_parts(sk_ristretto_expanded_ptr as *const u8, 64) }; let pk = unsafe { from_raw_parts(pk_ptr as *const u8, 32) }; @@ -131,6 +145,15 @@ pub extern "C" fn sign_sr25519_phase2( ) { c_zemu_log_stack(b"sign_sr25519\x00".as_ref()); + if sk_ristretto_expanded_ptr.is_null() + || pk_ptr.is_null() + || context_ptr.is_null() + || msg_ptr.is_null() + || sig_ptr.is_null() + { + return; + } + let sk_ristretto_expanded = unsafe { from_raw_parts(sk_ristretto_expanded_ptr as *const u8, 64) }; let pk = unsafe { from_raw_parts(pk_ptr as *const u8, 32) }; @@ -158,6 +181,9 @@ pub extern "C" fn sign_sr25519_phase2( #[no_mangle] pub extern "C" fn expanded_sr25519_sk(sk_ed25519_ptr: *mut u8, sk_ed25519_expanded_ptr: *mut u8) { + if sk_ed25519_ptr.is_null() || sk_ed25519_expanded_ptr.is_null() { + return; + } let sk_ed25519 = unsafe { from_raw_parts_mut(sk_ed25519_ptr as *mut u8, 32) }; let sk_ed25519_expanded = unsafe { from_raw_parts_mut(sk_ed25519_expanded_ptr as *mut u8, 64) }; let secret: MiniSecretKey = MiniSecretKey::from_bytes(&sk_ed25519[..]).unwrap_handler(); @@ -166,6 +192,9 @@ pub extern "C" fn expanded_sr25519_sk(sk_ed25519_ptr: *mut u8, sk_ed25519_expand #[no_mangle] pub extern "C" fn get_sr25519_sk(sk_ed25519_expanded_ptr: *mut u8) { + if sk_ed25519_expanded_ptr.is_null() { + return; + } let sk_ed25519_expanded = unsafe { from_raw_parts_mut(sk_ed25519_expanded_ptr as *mut u8, 64) }; let secret: SecretKey = SecretKey::from_ed25519_bytes(&sk_ed25519_expanded[..]).unwrap_handler(); diff --git a/app/src/apdu_handler.c b/app/src/apdu_handler.c index 75879197..5c6c0c0b 100644 --- a/app/src/apdu_handler.c +++ b/app/src/apdu_handler.c @@ -37,10 +37,19 @@ static bool tx_initialized = false; +// Storage for the review-pending re-entrancy lock declared in actions.h. +// Gates handleApdu so no new APDU can mutate tx / hdPath / G_io_apdu_buffer +// state while a user review is on screen (BLE transport in particular has +// no equivalent SDK-level guard). +volatile bool g_review_pending = false; + static const char *msg_error1 = "Expert Mode"; static const char *msg_error2 = "Required"; void extractHDPath(uint32_t rx, uint32_t offset) { + if (rx < offset) { + THROW(APDU_CODE_WRONG_LENGTH); + } MEMZERO(hdPath, sizeof(hdPath)); if ((rx - offset) == sizeof(uint32_t) * HDPATH_LEN_ADR0008) { hdPathLen = HDPATH_LEN_ADR0008; @@ -64,7 +73,12 @@ void extractHDPath(uint32_t rx, uint32_t offset) { THROW(APDU_CODE_DATA_INVALID); } - if (hdPathLen == HDPATH_LEN_ADR0008 && hdPath[2] < 0x80000000) { + // Account index (BIP44 component 2) must be hardened for both the + // 3-element ADR-0008 and the 5-element default shapes. Leaving the + // account index unhardened would let a host request signatures under + // a non-BIP44 account, providing a silent address-enumeration + // primitive when paired with INS_GET_ADDR_* with no confirmation. + if (hdPath[2] < 0x80000000) { THROW(APDU_CODE_DATA_INVALID); } } @@ -72,6 +86,14 @@ void extractHDPath(uint32_t rx, uint32_t offset) { void extract_eth_path(uint32_t rx, uint32_t offset) { tx_initialized = false; + // Reject before reading the path-length byte: rx == offset is + // reachable with a truncated APDU (rx == APDU_MIN_LENGTH == + // OFFSET_DATA), which would otherwise read past the received data + // and underflow the length check below. + if (rx <= offset) { + THROW(APDU_CODE_WRONG_LENGTH); + } + uint32_t path_len = *(G_io_apdu_buffer + offset); if (path_len > MAX_BIP32_PATH || path_len < 1) { @@ -97,6 +119,14 @@ void extract_eth_path(uint32_t rx, uint32_t offset) { THROW(APDU_CODE_DATA_INVALID); } + // Require the account index to be hardened when the path reaches + // component 2, matching the invariant enforced on the consensus side + // (extractHDPath above) and closing the unhardened-account + // enumeration primitive on EVM-side GET_ADDR. + if (path_len >= 3 && hdPath[2] < 0x80000000) { + THROW(APDU_CODE_DATA_INVALID); + } + // set the hdPath len hdPathLen = path_len; } @@ -449,6 +479,14 @@ void handleApdu(volatile uint32_t *flags, volatile uint32_t *tx, uint32_t rx) { BEGIN_TRY { TRY { + // Reject every incoming APDU while a user review is pending. + // Every terminal callback in actions.c clears the flag; a + // concurrent APDU landing during the review window throws here + // without touching shared state. + if (review_is_pending()) { + THROW(APDU_CODE_COMMAND_NOT_ALLOWED); + } + uint8_t cla = G_io_apdu_buffer[OFFSET_CLA]; if ((cla != CLA) && (cla != CLA_ETH)) { THROW(APDU_CODE_CLA_NOT_SUPPORTED); @@ -524,8 +562,18 @@ void handleApdu(volatile uint32_t *flags, volatile uint32_t *tx, uint32_t rx) { default: THROW(APDU_CODE_INS_NOT_SUPPORTED); } + + // If the dispatched handler entered an async user review, + // arm the re-entrancy lock. Cleared by each terminal + // callback in actions.c. + if ((*flags & IO_ASYNCH_REPLY) != 0) { + review_mark_pending(); + } + } + CATCH(EXCEPTION_IO_RESET) { + review_clear_pending(); + THROW(EXCEPTION_IO_RESET); } - CATCH(EXCEPTION_IO_RESET) { THROW(EXCEPTION_IO_RESET); } CATCH_OTHER(e) { switch (e & 0xF000) { case 0x6000: @@ -536,6 +584,13 @@ void handleApdu(volatile uint32_t *flags, volatile uint32_t *tx, uint32_t rx) { sw = 0x6800 | (e & 0x7FF); break; } + // Clear the review lock on every error path except the + // one that rejected an incoming APDU while a review was + // pending — in that case the legitimate in-flight review + // must keep its lock. + if (e != APDU_CODE_COMMAND_NOT_ALLOWED) { + review_clear_pending(); + } G_io_apdu_buffer[*tx] = sw >> 8; G_io_apdu_buffer[*tx + 1] = sw; *tx += 2; diff --git a/app/src/common/actions.c b/app/src/common/actions.c index da6605fc..53906b7e 100644 --- a/app/src/common/actions.c +++ b/app/src/common/actions.c @@ -31,6 +31,8 @@ uint16_t action_addrResponseLen; void app_sign_ed25519() { + review_clear_pending(); + uint8_t *signature = G_io_apdu_buffer; uint16_t replyLen = 0; @@ -49,6 +51,8 @@ void app_sign_ed25519() { } void app_sign_secp256k1() { + review_clear_pending(); + uint8_t *signature = G_io_apdu_buffer; uint16_t replyLen = 0; @@ -67,6 +71,8 @@ void app_sign_secp256k1() { } void app_sign_sr25519() { + review_clear_pending(); + uint8_t *signature = G_io_apdu_buffer; uint8_t messageDigest[CX_SHA512_SIZE] = {0}; size_t ctx_len = 0; @@ -87,6 +93,7 @@ void app_sign_sr25519() { } void app_reject() { + review_clear_pending(); MEMZERO(G_io_apdu_buffer, IO_APDU_BUFFER_SIZE); set_code(G_io_apdu_buffer, 0, APDU_CODE_COMMAND_NOT_ALLOWED); io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); @@ -109,6 +116,8 @@ zxerr_t app_fill_address(address_kind_e kind) { } void app_sign_eth() { + review_clear_pending(); + const uint8_t *message = tx_get_buffer(); const uint16_t messageLength = tx_get_buffer_length(); uint16_t replyLen = 0; @@ -126,6 +135,7 @@ void app_sign_eth() { } void app_reply_address() { + review_clear_pending(); if (action_addrResponseLen == 0) { THROW(APDU_CODE_DATA_INVALID); } @@ -134,6 +144,7 @@ void app_reply_address() { } void app_reply_error() { + review_clear_pending(); set_code(G_io_apdu_buffer, 0, APDU_CODE_DATA_INVALID); io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); } diff --git a/app/src/common/actions.h b/app/src/common/actions.h index fd4d577a..9d4f0115 100644 --- a/app/src/common/actions.h +++ b/app/src/common/actions.h @@ -15,6 +15,7 @@ ********************************************************************************/ #pragma once +#include #include #include "coin.h" @@ -22,6 +23,12 @@ extern uint16_t action_addrResponseLen; +extern volatile bool g_review_pending; + +static inline bool review_is_pending(void) { return g_review_pending; } +static inline void review_mark_pending(void) { g_review_pending = true; } +static inline void review_clear_pending(void) { g_review_pending = false; } + void app_sign_ed25519(); void app_sign_secp256k1(); void app_sign_sr25519(); diff --git a/app/src/common/tx.c b/app/src/common/tx.c index 5e28f3ad..1109d215 100644 --- a/app/src/common/tx.c +++ b/app/src/common/tx.c @@ -99,7 +99,7 @@ zxerr_t tx_getItem(int8_t displayIdx, char *outKey, uint16_t outKeyLen, char *ou CHECK_ZXERR(tx_getNumItems(&numItems)) - if (displayIdx < 0 || displayIdx > numItems) { + if (displayIdx < 0 || displayIdx >= numItems) { return zxerr_no_data; } diff --git a/app/src/consumer/cbor_helper.h b/app/src/consumer/cbor_helper.h index 18bb37dd..416645f0 100644 --- a/app/src/consumer/cbor_helper.h +++ b/app/src/consumer/cbor_helper.h @@ -25,6 +25,7 @@ __Z_INLINE parser_error_t parser_mapCborError(CborError err) { case CborErrorUnexpectedEOF: return parser_cbor_unexpected_EOF; case CborErrorMapNotSorted: + case CborErrorMapKeysNotUnique: return parser_cbor_not_canonical; case CborNoError: return parser_ok; diff --git a/app/src/consumer/coin_consumer.h b/app/src/consumer/coin_consumer.h index 0199006f..507f004c 100644 --- a/app/src/consumer/coin_consumer.h +++ b/app/src/consumer/coin_consumer.h @@ -74,6 +74,8 @@ extern "C" { #define COIN_RATE_DECIMAL_PLACES 5 #define MAX_RATES 10 +#define MAX_BOUNDS 16 +#define MAX_TOKENS 16 #define MAX_CONTEXT_SIZE 64 #define MAX_ENTITY_NODES 16 diff --git a/app/src/consumer/parser_consumer.c b/app/src/consumer/parser_consumer.c index 5a323cfe..1baf7807 100644 --- a/app/src/consumer/parser_consumer.c +++ b/app/src/consumer/parser_consumer.c @@ -365,7 +365,10 @@ parser_error_t parser_printInnerField(ui_field_t *ui_field) { if (elements_print == 1) { cbor_value_advance(¤t); - snprintf(val + offset, SCREEN_SIZE, "%s", ":"); + // Bound the colon-separator write against remaining buffer space + // (val[SCREEN_SIZE]); SCREEN_SIZE alone would let the write spill + // past the end of the stack buffer once offset approached SCREEN_SIZE. + snprintf(val + offset, SCREEN_SIZE - offset, "%s", ":"); offset += 1; } } @@ -1447,8 +1450,8 @@ __Z_INLINE parser_error_t parser_printStakingAmendCommissionSchedule(const parse } uint8_t dynDisplayIdx = displayIdx - 1; - if (dynDisplayIdx < (int)(parser_tx_obj.oasis.tx.body.stakingAmendCommissionSchedule.rates_length * 2 + - parser_tx_obj.oasis.tx.body.stakingAmendCommissionSchedule.bounds_length * 3)) { + if (dynDisplayIdx < (int)((parser_tx_obj.oasis.tx.body.stakingAmendCommissionSchedule.rates_length * 2) + + (parser_tx_obj.oasis.tx.body.stakingAmendCommissionSchedule.bounds_length * 3))) { if (dynDisplayIdx / 2 < (int)parser_tx_obj.oasis.tx.body.stakingAmendCommissionSchedule.rates_length) { const int8_t index = dynDisplayIdx / 2; commissionRateStep_t rate; @@ -1494,8 +1497,8 @@ __Z_INLINE parser_error_t parser_printStakingAmendCommissionSchedule(const parse } } - uint8_t lastDisplayIdx = dynDisplayIdx - parser_tx_obj.oasis.tx.body.stakingAmendCommissionSchedule.rates_length * 2 - - parser_tx_obj.oasis.tx.body.stakingAmendCommissionSchedule.bounds_length * 3; + uint8_t lastDisplayIdx = dynDisplayIdx - (parser_tx_obj.oasis.tx.body.stakingAmendCommissionSchedule.rates_length * 2) - + (parser_tx_obj.oasis.tx.body.stakingAmendCommissionSchedule.bounds_length * 3); switch (lastDisplayIdx) { case 0: { snprintf(outKey, outKeyLen, "Fee"); diff --git a/app/src/consumer/parser_impl_con.c b/app/src/consumer/parser_impl_con.c index d8b1fe8e..e6b1de14 100644 --- a/app/src/consumer/parser_impl_con.c +++ b/app/src/consumer/parser_impl_con.c @@ -432,6 +432,9 @@ __Z_INLINE parser_error_t _readAmendment(parser_tx_t *v, CborValue *value) { // Array of rates cbor_value_get_array_length(&contents, &v->oasis.tx.body.stakingAmendCommissionSchedule.rates_length); + if (v->oasis.tx.body.stakingAmendCommissionSchedule.rates_length > MAX_RATES) { + return parser_unexpected_number_items; + } CHECK_CBOR_ERR(cbor_value_advance(&contents)) @@ -441,6 +444,9 @@ __Z_INLINE parser_error_t _readAmendment(parser_tx_t *v, CborValue *value) { // Array of bounds cbor_value_get_array_length(&contents, &v->oasis.tx.body.stakingAmendCommissionSchedule.bounds_length); + if (v->oasis.tx.body.stakingAmendCommissionSchedule.bounds_length > MAX_BOUNDS) { + return parser_unexpected_number_items; + } return parser_ok; } @@ -749,6 +755,30 @@ __Z_INLINE parser_error_t _readBody(parser_tx_t *v, CborValue *rootItem) { CHECK_CBOR_ERR(cbor_value_advance(&upgradeVal)) CHECK_PARSER_ERR(_readString( &upgradeVal, (uint8_t *)&v->oasis.tx.body.governanceSubmitProposal.upgrade.handler, HANDLER_MAX_LENGTH)); + + // Reject embedded NUL bytes in the handler value. The display + // path uses `snprintf("%s", handler)` which walks the buffer + // as a C string, so an embedded NUL would truncate the + // user-visible text while the signed CBOR covers the full + // field. The enclosing oasis_tx_t was MEMZERO'd in _read, so + // any non-zero byte after the first NUL in the 32-byte + // buffer must have been supplied by the CBOR payload itself. + { + const uint8_t *h = v->oasis.tx.body.governanceSubmitProposal.upgrade.handler; + size_t handler_end = HANDLER_MAX_LENGTH; + for (size_t i = 0; i < HANDLER_MAX_LENGTH; i++) { + if (h[i] == 0) { + handler_end = i; + break; + } + } + for (size_t i = handler_end + 1; i < HANDLER_MAX_LENGTH; i++) { + if (h[i] != 0) { + return parser_unexpected_characters; + } + } + } + CHECK_CBOR_ERR(cbor_value_advance(&upgradeVal)) v->oasis.tx.body.governanceSubmitProposal.type = upgrade; @@ -1303,6 +1333,9 @@ __Z_INLINE parser_error_t _readRuntimeContractsBody(parser_tx_t *v, CborValue *r // Array of tokens cbor_value_get_array_length(&tokensField, &v->oasis.runtime.call.body.contracts.tokensLen); + if (v->oasis.runtime.call.body.contracts.tokensLen > MAX_TOKENS) { + return parser_unexpected_number_items; + } return parser_ok; } @@ -1809,7 +1842,11 @@ parser_error_t _read(const parser_context_t *c, parser_tx_t *v) { CborValue rootItem = {0}; INIT_CBOR_PARSER(c, rootItem) - CHECK_CBOR_ERR(cbor_value_validate_basic(&rootItem)) + // Enforce canonical CBOR (RFC 8949 §4.2.1) plus map-key uniqueness so + // any non-canonical or duplicate-key input — which the chain-side + // serializer would never produce — is rejected before it can drive a + // sign-different-than-displayed parity break. + CHECK_CBOR_ERR(cbor_value_validate(&rootItem, CborValidateCanonicalFormat | CborValidateMapKeysAreUnique)) if (cbor_value_at_end(&rootItem)) { return parser_unexpected_buffer_end; @@ -1843,6 +1880,14 @@ parser_error_t _read(const parser_context_t *c, parser_tx_t *v) { return parser_context_unknown_prefix; } + // The signing path hashes the full received buffer, so any bytes after + // the parsed CBOR map would enter the signature without being displayed. + // Advance past the root map and require the cursor to land at end-of-stream. + CHECK_CBOR_ERR(cbor_value_advance(&rootItem)) + if (!cbor_value_at_end(&rootItem)) { + return parser_unexpected_buffer_end; + } + return parser_ok; } diff --git a/app/src/crypto.c b/app/src/crypto.c index f70af2bb..e6736dc1 100644 --- a/app/src/crypto.c +++ b/app/src/crypto.c @@ -206,9 +206,17 @@ zxerr_t crypto_signSecp256k1(uint8_t *output, uint16_t outputLen, const uint8_t const err_convert_e err_c = convertDERtoRSV(signature_object->der_signature, info, signature_object->r, signature_object->s, &signature_object->v); + // Explicit failure branch. Without the else, `error` would keep the + // last value set on the success path and the function would return + // zxerr_ok with a signature buffer left in an undefined state; the + // cleanup gate below also keys off `error != zxerr_ok`, so secret + // zeroization would be skipped on the unreachable-but-possible DER + // conversion failure. if (err_c == no_error) { *sigSize = sizeof_field(signature_t, r) + sizeof_field(signature_t, s) + sizeof_field(signature_t, v); error = zxerr_ok; + } else { + error = zxerr_invalid_crypto_settings; } catch_cx_error: @@ -326,6 +334,10 @@ zxerr_t _sign(uint8_t *output, uint16_t outputLen, const uint8_t *message, uint1 *info = tmpInfo; } error = zxerr_ok; + } else { + // DER conversion failed; signal explicitly so the cleanup block + // below zeroises `output` and the caller sees a non-ok return. + error = zxerr_invalid_crypto_settings; } catch_cx_error: diff --git a/app/src/parser_impl_eth.c b/app/src/parser_impl_eth.c index 20b9720b..7fab5312 100644 --- a/app/src/parser_impl_eth.c +++ b/app/src/parser_impl_eth.c @@ -74,7 +74,7 @@ static parser_error_t readChainID(parser_context_t *ctx, chain_id_t *chain_id) { } static parser_error_t readBigInt(parser_context_t *ctx, eth_big_int_t *big_int) { - if (parse_field(ctx, &(big_int->offset), &(big_int->len)) != rlp_ok) { + if (parse_field(ctx, &(big_int->offset), &(big_int->len)) != parser_ok) { return parser_invalid_rlp_data; } diff --git a/deps/ledger-zxlib b/deps/ledger-zxlib index e01a96ae..36c27b3b 160000 --- a/deps/ledger-zxlib +++ b/deps/ledger-zxlib @@ -1 +1 @@ -Subproject commit e01a96ae23bbd9acc8d20e14bf206888e0023389 +Subproject commit 36c27b3be04d901498b471023a1efab431ff22ab diff --git a/tests_zemu/jest.config.js b/tests_zemu/jest.config.js index 1089b96d..48390a5c 100644 --- a/tests_zemu/jest.config.js +++ b/tests_zemu/jest.config.js @@ -1,7 +1,18 @@ module.exports = { preset: 'ts-jest', testEnvironment: 'node', - transformIgnorePatterns: ['^.+\\.js$'], + // `@zondax/zemu` transitively imports `get-port`, which ships as pure ESM + // (`import ... from 'node:net'`). Let Jest transform it (and other packages + // we may need to opt in later); keep the rest of node_modules ignored. + transformIgnorePatterns: ['node_modules/(?!(get-port)/)'], + // Explicit .ts + .js transforms. The .js entry downlevels the ESM inside + // the allow-listed node_modules above — plus the co-located @zondax/ledger-oasis + // dist/*.js — to CJS so Jest's module loader can require() them. `allowJs` + // and `isolatedModules` live in tsconfig.json per ts-jest v30 conventions. + transform: { + '^.+\\.ts$': ['ts-jest', {}], + '^.+\\.js$': ['ts-jest', {}], + }, reporters: ['default', ['summary', { summaryThreshold: 1 }]], globalSetup: './globalsetup.js', clearMocks: true, diff --git a/tests_zemu/package.json b/tests_zemu/package.json index 576e7091..5d4f1301 100644 --- a/tests_zemu/package.json +++ b/tests_zemu/package.json @@ -25,7 +25,7 @@ }, "dependencies": { "@zondax/ledger-oasis": "file:../js", - "@zondax/zemu": "^0.65.0" + "@zondax/zemu": "^0.66.2" }, "devDependencies": { "@ethereumjs/common": "^2.4.0", @@ -38,7 +38,7 @@ "@typescript-eslint/eslint-plugin": "^8.50.0", "@typescript-eslint/parser": "^8.50.0", "blakejs": "^1.2.1", - "bn.js": "^5.2.2", + "bn.js": "^5.2.3", "crypto-js": "4.2.0", "ed25519-supercop": "^2.0.1", "elliptic": "^6.6.1", @@ -46,8 +46,8 @@ "eslint-config-prettier": "^10.1.8", "eslint-plugin-import": "^2.32.0", "eslint-plugin-jest": "^29.5.0", - "eslint-plugin-prettier": "^5.5.4", - "jest": "^30.2.0", + "eslint-plugin-prettier": "^5.5.5", + "jest": "^30.3.0", "js-sha256": "0.11.1", "js-sha512": "^0.9.0", "prettier": "^3.7.4", diff --git a/tests_zemu/snapshots/ap-adr0008-0-mainmenu/00004.png b/tests_zemu/snapshots/ap-adr0008-0-mainmenu/00004.png index 3fd0b5a9..1ad886f9 100644 Binary files a/tests_zemu/snapshots/ap-adr0008-0-mainmenu/00004.png and b/tests_zemu/snapshots/ap-adr0008-0-mainmenu/00004.png differ diff --git a/tests_zemu/snapshots/ap-adr0008-5-mainmenu/00004.png b/tests_zemu/snapshots/ap-adr0008-5-mainmenu/00004.png index 3fd0b5a9..1ad886f9 100644 Binary files a/tests_zemu/snapshots/ap-adr0008-5-mainmenu/00004.png and b/tests_zemu/snapshots/ap-adr0008-5-mainmenu/00004.png differ diff --git a/tests_zemu/snapshots/ap-mainmenu/00004.png b/tests_zemu/snapshots/ap-mainmenu/00004.png index 3fd0b5a9..1ad886f9 100644 Binary files a/tests_zemu/snapshots/ap-mainmenu/00004.png and b/tests_zemu/snapshots/ap-mainmenu/00004.png differ diff --git a/tests_zemu/snapshots/fl-adr0008-0-mainmenu/00004.png b/tests_zemu/snapshots/fl-adr0008-0-mainmenu/00004.png index 55417e5f..183dd771 100644 Binary files a/tests_zemu/snapshots/fl-adr0008-0-mainmenu/00004.png and b/tests_zemu/snapshots/fl-adr0008-0-mainmenu/00004.png differ diff --git a/tests_zemu/snapshots/fl-adr0008-5-mainmenu/00004.png b/tests_zemu/snapshots/fl-adr0008-5-mainmenu/00004.png index 55417e5f..183dd771 100644 Binary files a/tests_zemu/snapshots/fl-adr0008-5-mainmenu/00004.png and b/tests_zemu/snapshots/fl-adr0008-5-mainmenu/00004.png differ diff --git a/tests_zemu/snapshots/fl-mainmenu/00004.png b/tests_zemu/snapshots/fl-mainmenu/00004.png index 55417e5f..183dd771 100644 Binary files a/tests_zemu/snapshots/fl-mainmenu/00004.png and b/tests_zemu/snapshots/fl-mainmenu/00004.png differ diff --git a/tests_zemu/snapshots/sp-adr0008-0-mainmenu/00004.png b/tests_zemu/snapshots/sp-adr0008-0-mainmenu/00004.png index a235d81e..ef2f3ca4 100644 Binary files a/tests_zemu/snapshots/sp-adr0008-0-mainmenu/00004.png and b/tests_zemu/snapshots/sp-adr0008-0-mainmenu/00004.png differ diff --git a/tests_zemu/snapshots/sp-adr0008-0-mainmenu/00010.png b/tests_zemu/snapshots/sp-adr0008-0-mainmenu/00010.png index a235d81e..ef2f3ca4 100644 Binary files a/tests_zemu/snapshots/sp-adr0008-0-mainmenu/00010.png and b/tests_zemu/snapshots/sp-adr0008-0-mainmenu/00010.png differ diff --git a/tests_zemu/snapshots/sp-adr0008-5-mainmenu/00004.png b/tests_zemu/snapshots/sp-adr0008-5-mainmenu/00004.png index a235d81e..ef2f3ca4 100644 Binary files a/tests_zemu/snapshots/sp-adr0008-5-mainmenu/00004.png and b/tests_zemu/snapshots/sp-adr0008-5-mainmenu/00004.png differ diff --git a/tests_zemu/snapshots/sp-adr0008-5-mainmenu/00010.png b/tests_zemu/snapshots/sp-adr0008-5-mainmenu/00010.png index a235d81e..ef2f3ca4 100644 Binary files a/tests_zemu/snapshots/sp-adr0008-5-mainmenu/00010.png and b/tests_zemu/snapshots/sp-adr0008-5-mainmenu/00010.png differ diff --git a/tests_zemu/snapshots/sp-mainmenu/00004.png b/tests_zemu/snapshots/sp-mainmenu/00004.png index a235d81e..ef2f3ca4 100644 Binary files a/tests_zemu/snapshots/sp-mainmenu/00004.png and b/tests_zemu/snapshots/sp-mainmenu/00004.png differ diff --git a/tests_zemu/snapshots/sp-mainmenu/00010.png b/tests_zemu/snapshots/sp-mainmenu/00010.png index a235d81e..ef2f3ca4 100644 Binary files a/tests_zemu/snapshots/sp-mainmenu/00010.png and b/tests_zemu/snapshots/sp-mainmenu/00010.png differ diff --git a/tests_zemu/snapshots/st-adr0008-0-mainmenu/00004.png b/tests_zemu/snapshots/st-adr0008-0-mainmenu/00004.png index 1903497b..ec8d48f6 100644 Binary files a/tests_zemu/snapshots/st-adr0008-0-mainmenu/00004.png and b/tests_zemu/snapshots/st-adr0008-0-mainmenu/00004.png differ diff --git a/tests_zemu/snapshots/st-adr0008-5-mainmenu/00004.png b/tests_zemu/snapshots/st-adr0008-5-mainmenu/00004.png index 1903497b..ec8d48f6 100644 Binary files a/tests_zemu/snapshots/st-adr0008-5-mainmenu/00004.png and b/tests_zemu/snapshots/st-adr0008-5-mainmenu/00004.png differ diff --git a/tests_zemu/snapshots/st-mainmenu/00004.png b/tests_zemu/snapshots/st-mainmenu/00004.png index 1903497b..ec8d48f6 100644 Binary files a/tests_zemu/snapshots/st-mainmenu/00004.png and b/tests_zemu/snapshots/st-mainmenu/00004.png differ diff --git a/tests_zemu/snapshots/x-adr0008-0-mainmenu/00004.png b/tests_zemu/snapshots/x-adr0008-0-mainmenu/00004.png index a235d81e..ef2f3ca4 100644 Binary files a/tests_zemu/snapshots/x-adr0008-0-mainmenu/00004.png and b/tests_zemu/snapshots/x-adr0008-0-mainmenu/00004.png differ diff --git a/tests_zemu/snapshots/x-adr0008-0-mainmenu/00010.png b/tests_zemu/snapshots/x-adr0008-0-mainmenu/00010.png index a235d81e..ef2f3ca4 100644 Binary files a/tests_zemu/snapshots/x-adr0008-0-mainmenu/00010.png and b/tests_zemu/snapshots/x-adr0008-0-mainmenu/00010.png differ diff --git a/tests_zemu/snapshots/x-adr0008-5-mainmenu/00004.png b/tests_zemu/snapshots/x-adr0008-5-mainmenu/00004.png index a235d81e..ef2f3ca4 100644 Binary files a/tests_zemu/snapshots/x-adr0008-5-mainmenu/00004.png and b/tests_zemu/snapshots/x-adr0008-5-mainmenu/00004.png differ diff --git a/tests_zemu/snapshots/x-adr0008-5-mainmenu/00010.png b/tests_zemu/snapshots/x-adr0008-5-mainmenu/00010.png index a235d81e..ef2f3ca4 100644 Binary files a/tests_zemu/snapshots/x-adr0008-5-mainmenu/00010.png and b/tests_zemu/snapshots/x-adr0008-5-mainmenu/00010.png differ diff --git a/tests_zemu/snapshots/x-mainmenu/00004.png b/tests_zemu/snapshots/x-mainmenu/00004.png index a235d81e..ef2f3ca4 100644 Binary files a/tests_zemu/snapshots/x-mainmenu/00004.png and b/tests_zemu/snapshots/x-mainmenu/00004.png differ diff --git a/tests_zemu/snapshots/x-mainmenu/00010.png b/tests_zemu/snapshots/x-mainmenu/00010.png index a235d81e..ef2f3ca4 100644 Binary files a/tests_zemu/snapshots/x-mainmenu/00010.png and b/tests_zemu/snapshots/x-mainmenu/00010.png differ diff --git a/tests_zemu/tsconfig.json b/tests_zemu/tsconfig.json index 3435f95c..82fbc293 100644 --- a/tests_zemu/tsconfig.json +++ b/tests_zemu/tsconfig.json @@ -6,7 +6,9 @@ "declaration": true, "forceConsistentCasingInFileNames": true, "skipLibCheck": true, - "outDir": "./dist" + "outDir": "./dist", + "allowJs": true, + "isolatedModules": true }, "exclude": [ "node_modules",