From 3b56b5ddcb67af8ea2e0fa08cfbbf8d8e3f79fbf Mon Sep 17 00:00:00 2001 From: Andrei Bacescu <9010245+bacescuandrei@users.noreply.github.com> Date: Tue, 27 Oct 2026 18:45:47 +0200 Subject: [PATCH] fix: [tools] Validation of mac-eui-64, mac-address and SHA256 ssh-fingerprint values mac-eui-64 values were truncated to 6 octets and checked with a wrong regex, so they never validated; mac-address values longer than 6 octets were truncated and accepted; SHA256 ssh-fingerprints in the unpadded form printed by OpenSSH were rejected. Whitespace in SHA256 fingerprints is ignored, as in MISP. The tests now require validate() to return True instead of any truthy value. --- pymisp/tools/attributevalidationtool.py | 9 ++++++--- tests/test_attributevalidationtool.py | 17 +++++++++++++---- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/pymisp/tools/attributevalidationtool.py b/pymisp/tools/attributevalidationtool.py index 8569a3c6c..6e22dc33c 100644 --- a/pymisp/tools/attributevalidationtool.py +++ b/pymisp/tools/attributevalidationtool.py @@ -109,7 +109,7 @@ DOMAIN_RE = re.compile(r'^[A-Z0-9.\-_]+\.[A-Z0-9\-]{2,}$', flags=re.IGNORECASE) HEX_RE = re.compile(r'^[0-9a-fA-F]+$') MAC_ADDRESS_RE = re.compile(r'^([a-f0-9]{2}:){5}[a-f0-9]{2}$') -MAC_EUI_64_RE = re.compile(r'^([a-f0-9]{2}:){3}ff:fe:(:[a-f0-9]{2}){3}$') +MAC_EUI_64_RE = re.compile(r'^([a-f0-9]{2}:){3}ff:fe(:[a-f0-9]{2}){3}$') ONION_RE = re.compile(r'^([a-z2-7]{16}|[a-z2-7]{56})\.onion$') REMOVE_NON_ALPHANUM_CAP_RE = re.compile(r'[^0-9A-Z]+') REMOVE_NON_ALPHANUM_RE = re.compile(r'[^0-9A-Fa-f]') @@ -219,7 +219,7 @@ def modifyBeforeValidation(cls, attribute_type, value): return value case 'mac-address' | 'mac-eui-64': value = re.sub(REMOVE_NON_ALPHANUM_RE, '', value).lower() - return ':'.join(value[i:i+2] for i in range(0, 12, 2)) + return ':'.join(value[i:i+2] for i in range(0, len(value), 2)) case 'hostname|port': return value.replace(':', '|').lower() case 'boolean': @@ -589,8 +589,11 @@ def _is_ssdeep(value): @classmethod def _is_ssh_fingerprint(cls, value): if value.startswith('SHA256:'): + # MISP ignores whitespace in the base64 encoded fingerprint + fingerprint = ''.join(value[7:].split()) try: - decoded = b64decode(value[7:]) + # OpenSSH prints SHA256 fingerprints without the base64 padding + decoded = b64decode(fingerprint + '=' * (-len(fingerprint) % 4), validate=True) except Exception: return False return decoded is not None and len(decoded) == 32 diff --git a/tests/test_attributevalidationtool.py b/tests/test_attributevalidationtool.py index bf2afee69..b927d3ae2 100644 --- a/tests/test_attributevalidationtool.py +++ b/tests/test_attributevalidationtool.py @@ -11,7 +11,7 @@ class TestAttributeValidationTool(unittest.TestCase): def _should_be_valid(self, type_, *values): for value in values: - self.assertTrue(AttributeValidationTool.validate(type_, value)) + self.assertIs(AttributeValidationTool.validate(type_, value), True) def _should_be_invalid(self, type_, *values): for value in values: @@ -75,6 +75,8 @@ def test_modify_before_validation_hashes(self): def test_modify_before_validation_mac(self): self.assertEqual('aa:bb:cc:dd:ee:ff', AttributeValidationTool.modifyBeforeValidation('mac-address', 'AA-BB-CC-DD-EE-FF')) self.assertEqual('aa:bb:cc:dd:ee:ff', AttributeValidationTool.modifyBeforeValidation('mac-address', 'aabbccddeeff')) + self.assertEqual('aa:bb:cc:dd:ee:ff:00:11', AttributeValidationTool.modifyBeforeValidation('mac-address', 'AA-BB-CC-DD-EE-FF-00-11')) + self.assertEqual('aa:bb:cc:ff:fe:dd:ee:11', AttributeValidationTool.modifyBeforeValidation('mac-eui-64', 'AA-BB-CC-FF-FE-DD-EE-11')) def test_modify_before_validation_phone(self): self.assertEqual('+123456', AttributeValidationTool.modifyBeforeValidation('phone-number', '00123456')) @@ -99,8 +101,8 @@ def test_modify_before_validation_x509(self): self.assertEqual('aa6fc83f37787abea6be2c5126163fd3', AttributeValidationTool.modifyBeforeValidation('x509-fingerprint-md5', 'AA:6F:C8:3F:37:78:7A:BE:A6:BE:2C:51:26:16:3F:D3')) def test_validate_as(self): - self._should_be_valid('AS', '0', 0, 1, '1', 4294967295, '1.1') - self._should_be_invalid('AS', '1.2.3.4') + self._should_be_valid('AS', '0', 0, 1, '1', 4294967295) + self._should_be_invalid('AS', '1.1', '1.2.3.4') def test_validate_domain_ip(self): self._should_be_valid( @@ -217,7 +219,7 @@ def test_validate_networking(self): self._should_be_valid('ip-dst|port', '127.0.0.1|80', '::1|80') self._should_be_invalid('ip-dst|port', '127.0.0.1', '127.0.0.1|99999') - self._should_be_valid('onion-address', 'abcdefghijklmnop.onion', 'abcdefghijklmnopqrstuvwxyz234567abcdefghijklmnopqrstuvwxyz23.onion') + self._should_be_valid('onion-address', 'abcdefghijklmnop.onion', 'abcdefghijklmnopqrstuvwxyz234567abcdefghijklmnopqrstuvwx.onion') self._should_be_invalid('onion-address', 'invalid.onion', 'abc.onion') self._should_be_valid('mac-address', 'aa:bb:cc:dd:ee:ff') @@ -252,6 +254,13 @@ def test_validate_ssh_fingerprint(self): '7b:e5:6f:a7:f4:f9:81:62:5c:e3:1f:bf:8b:57:6c:5a', 'MD5:7b:e5:6f:a7:f4:f9:81:62:5c:e3:1f:bf:8b:57:6c:5a', 'SHA256:mVPwvezndPv/ARoIadVY98vAC0g+P/5633yTC4d/wXE', + 'SHA256:mVPwvezndPv/ARoIadVY98vAC0g+P/5633yTC4d/wXE=', + 'SHA256:mVPwvezndPv/ARoIadVY98vAC0g+P/5633 yTC4d/wXE', + ) + self._should_be_invalid( + 'ssh-fingerprint', + 'SHA256:mVPwvezndPv/ARoIadVY98vAC0g+P/5633yTC4d/wX', + 'SHA256:mVPwvezndPv/ARoIadVY98vAC0g+P/5633yTC4d/wX!', ) def test_validate_event(self):