From 8f946778ae5143ecee48e229acfcb4634bf0b357 Mon Sep 17 00:00:00 2001 From: Willi Ballenthin Date: Wed, 22 Apr 2026 09:16:07 +0300 Subject: [PATCH] fix: extract_os yields duplicate/contradictory OS values Closes #3017 --- CHANGELOG.md | 2 ++ capa/features/extractors/common.py | 1 + tests/test_os_detection.py | 18 ++++++++++++++++++ 3 files changed, 21 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc3bec14..f4612731 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,8 @@ - fix: vverbose.py: render_call variable assigned but never used @williballenthin #3016 +- fix: extract_os yields duplicate/contradictory OS values @williballenthin #3017 + ### capa Explorer Web ### capa Explorer IDA Pro plugin diff --git a/capa/features/extractors/common.py b/capa/features/extractors/common.py index dc744c29..35cbccab 100644 --- a/capa/features/extractors/common.py +++ b/capa/features/extractors/common.py @@ -123,6 +123,7 @@ def extract_arch(buf) -> Iterator[tuple[Feature, Address]]: def extract_os(buf, os=OS_AUTO) -> Iterator[tuple[Feature, Address]]: if os != OS_AUTO: yield OS(os), NO_ADDRESS + return if buf.startswith(MATCH_PE): yield OS(OS_WINDOWS), NO_ADDRESS diff --git a/tests/test_os_detection.py b/tests/test_os_detection.py index 4d71c67b..9dc6832d 100644 --- a/tests/test_os_detection.py +++ b/tests/test_os_detection.py @@ -19,6 +19,8 @@ from pathlib import Path from fixtures import get_data_path_by_name import capa.features.extractors.elf +import capa.features.extractors.common +from capa.features.common import OS_LINUX, OS_WINDOWS def test_elf_sh_notes(): @@ -181,3 +183,19 @@ def test_elf_parse_capa_pyinstaller_header(): ]) ) assert capa.features.extractors.elf.detect_elf_os(io.BytesIO(elf_header)) == "linux" + + +def test_extract_os_explicit_yields_exactly_one(): + pe_buf = b"MZ" + b"\x00" * 64 + results = list(capa.features.extractors.common.extract_os(pe_buf, os=OS_LINUX)) + assert len(results) == 1 + feature, _ = results[0] + assert feature.value == OS_LINUX + + +def test_extract_os_auto_pe_yields_exactly_one(): + pe_buf = b"MZ" + b"\x00" * 64 + results = list(capa.features.extractors.common.extract_os(pe_buf)) + assert len(results) == 1 + feature, _ = results[0] + assert feature.value == OS_WINDOWS