From 852cdeda0ba51a73c3d6b5e7662ad8c5fe5354ea Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sat, 29 Aug 2026 15:04:29 +0000 Subject: [PATCH] tx.unsigned: refuse to sign legacy (non-JSON) TX files Prevent signing of transactions from legacy TX files, which represent a potential vector for deserialization attacks. Since a normal up-to-date MMGen Wallet installation cannot create legacy TX files, it indicates the online installation is compromised or broken, so exit the signing loop as well and output a critical error message. --- .github/workflows/build.yaml | 2 ++ .github/workflows/lint.yaml | 2 ++ mmgen/autosign/__init__.py | 2 ++ mmgen/data/version | 2 +- mmgen/exception.py | 1 + mmgen/tx/unsigned.py | 7 ++++++- 6 files changed, 14 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index e45f6bdd..c92019ff 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -4,6 +4,8 @@ run-name: Build and install on: push: + branches: + - ci paths: - '.github/workflows/build.yaml' - '.github/build-requirements.txt' diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 1ca69810..6d2fceef 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -4,6 +4,8 @@ run-name: Check code with Ruff, Bandit and Pylint static analyzers on: push: + branches: + - ci paths: - '.github/workflows/lint.yaml' - '.github/build-requirements.txt' diff --git a/mmgen/autosign/__init__.py b/mmgen/autosign/__init__.py index d329a14a..dbb3dbda 100755 --- a/mmgen/autosign/__init__.py +++ b/mmgen/autosign/__init__.py @@ -272,6 +272,8 @@ class Autosign: except Exception as e: ymsg('An error occurred with {} ‘{}’:\n {}: ‘{}’'.format( target.desc, f.name, type(e).__name__, e)) + if type(e).__name__ == 'LegacyTxSignRequestError': + raise except: ymsg('An error occurred with {} ‘{}’'.format(target.desc, f.name)) good.append(ret) if ret else bad.append(f) diff --git a/mmgen/data/version b/mmgen/data/version index fd1bd70b..6b911fc8 100644 --- a/mmgen/data/version +++ b/mmgen/data/version @@ -1 +1 @@ -16.2.0 +16.3.0dev0 diff --git a/mmgen/exception.py b/mmgen/exception.py index cd759135..a409c795 100755 --- a/mmgen/exception.py +++ b/mmgen/exception.py @@ -86,6 +86,7 @@ class WalletFileError(Exception): mmcode = 3 class HexadecimalStringError(Exception): mmcode = 3 class SeedLengthError(Exception): mmcode = 3 class PrivateKeyError(Exception): mmcode = 3 +class LegacyTxSignRequestError(Exception):mmcode = 3 class MMGenCalledProcessError(Exception): mmcode = 3 class TestSuiteFatalException(Exception): mmcode = 3 diff --git a/mmgen/tx/unsigned.py b/mmgen/tx/unsigned.py index ba0fc507..597860e0 100755 --- a/mmgen/tx/unsigned.py +++ b/mmgen/tx/unsigned.py @@ -13,7 +13,7 @@ tx.unsigned: unsigned transaction class """ from .completed import Completed -from ..util import remove_dups +from ..util import die, remove_dups class Unsigned(Completed): desc = 'unsigned transaction' @@ -23,6 +23,11 @@ class Unsigned(Completed): # Return signed object or False. Don’t exit or raise exception, unless fatal: async def sign(self, keys, tx_num_str=''): + if self.file_format == 'legacy': # fatal + die('LegacyTxSignRequestError', + 'Request to sign legacy-format transaction. ' + 'Has your online installation been compromised?') + try: self.check_correct_chain() except Exception as e: