Indicators._process_indicator crashes on any STIX pattern whose value contains "="
Indicators._process_indicator crashes on any STIX pattern whose value contains =
Summary
_process_indicator() splits the STIX pattern on = and unpacks exactly two parts:
# src/mvt/common/indicators.py:119
key, value = indicator.get("pattern", "").strip("[]").split("=")Any indicator whose value contains an = raises ValueError: too many values to unpack. The exception is not caught in parse_stix2(), load_indicators_files() or _check_stix2_env_variable(), so it propagates out of the CLI: mvt-ios check-iocs / mvt-android check-iocs (and any --iocs run) abort with a traceback and no indicators from any file are loaded, not just the offending one.
The obvious trigger is a url:value indicator with a query string, e.g. [url:value='https://example.com/track?id=1']. url:value is a supported type, and one-click exploit-delivery links with query parameters are exactly the kind of URL people will want to add. file:path values can also legitimately contain =.
Reproduction
bad.stix2:
{"type":"bundle","id":"bundle--00000000-0000-4000-8000-000000000000","objects":[
{"type":"malware","spec_version":"2.1","id":"malware--00000000-0000-4000-8000-000000000001","created":"2026-01-01T00:00:00Z","modified":"2026-01-01T00:00:00Z","name":"Demo","is_family":false},
{"type":"indicator","spec_version":"2.1","id":"indicator--00000000-0000-4000-8000-000000000002","created":"2026-01-01T00:00:00Z","modified":"2026-01-01T00:00:00Z","indicator_types":["malicious-activity"],"pattern":"[domain-name:value='good-domain.example']","pattern_type":"stix","valid_from":"2026-01-01T00:00:00Z"},
{"type":"indicator","spec_version":"2.1","id":"indicator--00000000-0000-4000-8000-000000000003","created":"2026-01-01T00:00:00Z","modified":"2026-01-01T00:00:00Z","indicator_types":["malicious-activity"],"pattern":"[url:value='https://example.com/track?id=1']","pattern_type":"stix","valid_from":"2026-01-01T00:00:00Z"},
{"type":"relationship","spec_version":"2.1","id":"relationship--00000000-0000-4000-8000-000000000004","created":"2026-01-01T00:00:00Z","modified":"2026-01-01T00:00:00Z","relationship_type":"indicates","source_ref":"indicator--00000000-0000-4000-8000-000000000002","target_ref":"malware--00000000-0000-4000-8000-000000000001"},
{"type":"relationship","spec_version":"2.1","id":"relationship--00000000-0000-4000-8000-000000000005","created":"2026-01-01T00:00:00Z","modified":"2026-01-01T00:00:00Z","relationship_type":"indicates","source_ref":"indicator--00000000-0000-4000-8000-000000000003","target_ref":"malware--00000000-0000-4000-8000-000000000001"}
]}$ MVT_STIX2=bad.stix2 mvt-ios check-iocs ./results
...
File ".../mvt/common/indicators.py", line 72, in _check_stix2_env_variable
self.parse_stix2(path)
File ".../mvt/common/indicators.py", line 317, in parse_stix2
self._process_indicator(indicator, collection)
File ".../mvt/common/indicators.py", line 119, in _process_indicator
key, value = indicator.get("pattern", "").strip("[]").split("=")
^^^^^^^^^^
ValueError: too many values to unpack (expected 2)Same result via Indicators().load_indicators_files(["bad.stix2"]). Tested on MVT 2026.7.29; the line is unchanged on main.
Impact
Latent today: none of the feeds in indicators.yaml currently contain a value with = (checked mvt-indicators, AmnestyTech pegasus.stix2 / novispy.stix2, and ECHAP stalkerware.stix2). But the first contributor to add a URL with a query string to any published feed will break check-iocs for every user who runs download-iocs, with no way to recover other than deleting the file from the local indicators folder.
Suggested fix
Split only on the first
=:key, value = indicator.get("pattern", "").strip("[]").split("=", 1)Make
parse_stix2()resilient to a single bad indicator, in the same spirit as the existing JSON-decode handling: wrap the_process_indicator()call so a malformed pattern logs a warning with the indicator id and is skipped, instead of aborting the run.Add a case to
tests/common/test_indicators.pywith aurl:valuepattern containing?a=b, asserting the URL is loaded intact and the domain indicator in the same file is still loaded.
Related brittleness worth handling in the same change, if the pattern parsing is being touched anyway: _add_indicator() strips every ' from the value, so a value containing an apostrophe would be silently altered. A small regex over the expected shape (^\[(?P<key>[^=\s]+)\s*=\s*'(?P<value>.*)'\]$) would handle both cases and reject anything else explicitly.
Source: mvt-project/mvt