Commit Graph

6041 Commits

Author SHA1 Message Date
Willi Ballenthin
0fdfc7734c fix: correct wrong dict key in VMRay _compute_monitor_threads assertion
In `_compute_monitor_threads`, the uniqueness assertion indexed
`monitor_threads_by_monitor_process` by `thread_id` instead of
`process_id`. Because the dict is a `defaultdict(list)`, each lookup on
a novel thread ID creates a fresh empty list, making the assertion
vacuously true. Duplicate thread IDs within a process are never caught.

Line 242 immediately below uses the correct key `process_id` when
appending, so the data structure is populated correctly; only the guard
was broken.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 18:53:41 +03:00
Willi Ballenthin
183a26afb0 fix: add explicit assert_never import in cape extractor.py
`format_call` referenced `capa.helpers.assert_never` without importing
`capa.helpers`, causing a NameError at runtime whenever a call argument
had an unexpected type. The module-qualified reference relied on an
implicit import-order dependency from another module having already
imported `capa.helpers`. Replace with an explicit `from capa.helpers
import assert_never`, matching the pattern used in the sibling
`call.py`.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 18:51:10 +03:00
Willi Ballenthin
8966a03132 fix: stop mutating call.api in cape thread.get_calls
`get_calls` iterated `generate_symbols` and overwrote `call.api` with
each generated symbol name, then yielded a `CallHandle` wrapping the
same `call` object. Because the Pydantic model is shared by reference,
every previously-yielded handle ended up with `api` equal to the last
symbol generated in the final iteration.

The correct pattern (used in `call.py:61`) is to leave the model
untouched and let the call extractor expand symbol variants via
`generate_symbols`. `get_calls` now yields exactly one `CallHandle`
per call with the original `api` value preserved.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 18:47:39 +03:00
Willi Ballenthin
ec3aa4525d fix: use instruction_indices in is_security_cookie for single-instruction basic blocks
`is_security_cookie` computes the last address in a terminal basic block by
iterating `bb.instruction_index` and indexing `ir.end_index - 1`. The BinExport2
protobuf spec omits `end_index` for single-element ranges, so protobuf returns 0
as the default. `0 - 1 = -1`, and -1 is not a key in `insn_address_by_index`,
raising `KeyError`.

Use `BinExport2Index.instruction_indices` to enumerate instruction indices, which
already handles single-instruction ranges via `HasField("end_index")`.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 18:43:05 +03:00
Willi Ballenthin
6f96fc7d90 fix: guard get_operand_expressions against empty expression tree
`_build_expression_tree` already returns `[]` for the Ghidra bug where
an operand has no expressions (see
https://github.com/NationalSecurityAgency/ghidra/issues/6817), but
`get_operand_expressions` then called the recursive walker
unconditionally with `tree_index=0`, which indexed into the empty list
and raised `IndexError`. Add an early-return guard so callers receive
`[]` instead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 18:31:37 +03:00
Willi Ballenthin
2d008fb53e fix: add return after zero-offset yield in extract_insn_offset_features
`extract_insn_offset_features` in the x86/x64 BinExport2 extractor handled
zero-offset patterns (e.g. `mov [reg], reg`) in a nested branch but was
missing a `return` after yielding `Offset(0)` and `OperandOffset(0)`.
Execution then fell through to the general `mask_immediate` path, which read
`immediate` from the last-matched expression node (a register, not an
integer). Since that field defaults to 0, the function emitted duplicate
`Offset(0)` and `OperandOffset(0)` features for every such instruction.

Fix: add `return` after the two yields in the zero-pattern branch.

Tests: add `FEATURE_COUNT_TESTS_BE2_INTEL` covering `MOV [EDI], CX` at
0x401125 in mimikatz, asserting each of `Offset(0)` and `OperandOffset(1,0)`
is emitted exactly once.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 18:28:16 +03:00
Willi Ballenthin
f8e0ea7144 fix: format ValueError message in binexport2 extractor
`ValueError` takes a single message string. Passing a second argument stores both as a tuple in `args` without any string formatting, so the feature value never appears in the error message. Use an f-string so the feature value is interpolated into the message.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 18:21:47 +03:00
Willi Ballenthin
81c22bb28c fix: correct scale/displacement in get_operand_phrase_info 5-expression branch
In the 5-expression branch of get_operand_phrase_info, two cases used
expression3 (the OPERATOR node) where expression4 (the IMMEDIATE_INT
value) was intended:

- Base + (Index * Scale): scale was expression3 ('*' operator), should be expression4 (the numeric scale value)
- (Index * Scale) + Displacement: displacement was expression3 ('+' operator), should be expression4 (the numeric displacement value)

Tests added using mimikatz.exe_.ghidra.BinExport:
- 0x40194d: MOVZX DI, [EAX + ECX * 1] — verifies scale=1 as IMMEDIATE_INT
- 0x401fd4: JMP [EAX * 4 + switchdataD_00402017] — verifies displacement=4202519 as IMMEDIATE_INT

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 18:19:19 +03:00
Willi Ballenthin
87b132011c fix: use HasField to check call-graph edge vertex indices
`_index_vertex_edges` used truthiness checks (`if not edge.source_vertex_index`)
to skip edges with absent fields, but this also silently drops any edge whose
source or target is vertex index 0 — a valid vertex. Both fields are protobuf
optional integers, so the correct absent-field check is `HasField()`, consistent
with `_index_flow_graph_edges` and `_index_call_graph_vertices` in the same file.

In mimikatz.exe_.ida.BinExport, vertex 0 at 0x401000 has 2 callees and 1 caller
that were all being silently discarded.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 18:00:58 +03:00
Willi Ballenthin
1ab0492954 fix: change bare if to elif in get_backend_from_cli DRAKVUF branch
`get_backend_from_cli` had a bare `if` at the FORMAT_DRAKVUF branch where
`elif` was expected. After `if input_format == FORMAT_CAPE: return BACKEND_CAPE`,
the DRAKVUF branch opened a new `if` rather than continuing the chain with
`elif`. The remaining branches used `elif`/`else` correctly. There was no
functional bug because the FORMAT_CAPE branch returns early, but the break
in the chain was misleading and looked like a merge artifact.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 17:41:08 +03:00
Willi Ballenthin
243e6408b0 fix: close file handle in get_file_taste using a with statement
`get_file_taste` opened a file handle with `sample_path.open("rb").read(8)`,
discarding the file object without explicitly closing it. CPython reference-
counting closes it promptly in practice, but other implementations (PyPy,
Jython) and CPython under GC pressure may defer closure. Use a `with` statement
to guarantee the handle is released immediately after reading.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 17:38:45 +03:00
Willi Ballenthin
cb3376d9e2 fix: correct off-by-one in call_count debug log for dynamic analysis
`enumerate` is 0-based, so after the loop `call_count` held `n - 1` for
`n` calls processed. The debug log at the end of `find_thread_capabilities`
therefore reported one fewer event than was actually analyzed. Replace
`enumerate` with an explicit `call_count += 1` counter so the log is
accurate.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 17:33:06 +03:00
Willi Ballenthin
47760269f8 fix: correct capa/subscope-rule key in RuleMetadata.from_capa
`RuleMetadata.from_capa` used `rule.meta.get("capa/subscope", False)` and
`Field(False, alias="capa/subscope")`, but the actual key set by
`_extract_subscope_rules_rec` is `"capa/subscope-rule"`. This caused
`is_subscope_rule` to always be `False` in every `RuleMetadata` instance,
making downstream filters in `render/utils.py`, `render/vverbose.py`, and
`scripts/import-to-ida.py` ineffective (though subscope rules are already
excluded from `ResultDocument` before reaching those callers).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 17:29:38 +03:00
Willi Ballenthin
099c9d41b7 fix: Scopes.from_dict uses cls instead of self
`Scopes.from_dict` was decorated with `@classmethod` but named its first
parameter `self` instead of `cls`, and hard-coded `Scopes(...)` in the
return statement instead of `cls(...)`. This meant any subclass calling
`SubScopes.from_dict(...)` would get a `Scopes` instance back rather than
a `SubScopes` instance.

Rename the parameter to `cls` and use it in the return statement so
that subclasses receive the correct type.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 17:23:31 +03:00
Willi Ballenthin
38df342c4e fix: remove unreachable backports.functools_lru_cache fallback
`functools.lru_cache` has been in the standard library since Python 3.2.
The project requires Python >=3.10, so the `except ImportError` branch
importing `backports.functools_lru_cache` can never execute.

Remove the try/except block and keep only the direct stdlib import.
Also remove `types-backports` from dev dependencies, `backports` from
`[tool.deptry.known_first_party]`, and `types-backports` from the
DEP002 ignore list in `pyproject.toml`.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 17:18:39 +03:00
Willi Ballenthin
8924ecf03a fix: remove Python 2-only Result.__nonzero__
`Result.__nonzero__` is the Python 2 boolean hook; Python 3 calls
`__bool__`, which is already defined immediately above it.
`__nonzero__` is never invoked at runtime in Python 3 and adds noise
that misleads readers into thinking it serves a purpose.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 17:14:10 +03:00
Willi Ballenthin
70d2c13559 fix: add missing ELF branch in get_format_from_extension for .elf_ files
EXTENSIONS_ELF = "elf_" was defined but never used: get_format_from_extension
had branches for every other EXTENSIONS_* constant except ELF. Since .elf_
files are real test fixtures and a recognised input format, the fix is to add
the missing elif branch (and import FORMAT_ELF) rather than delete the
constant.

Closes #3031

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 17:11:39 +03:00
Moritz
74276c8c40 Merge pull request #3006 from mandiant/dependabot/pip/pydantic-2.13.0
build(deps): bump pydantic from 2.12.4 to 2.13.0
2026-04-17 15:23:57 +00:00
dependabot[bot]
e72f8baea6 build(deps): bump rich from 14.3.2 to 15.0.0 (#3007)
Bumps [rich](https://github.com/Textualize/rich) from 14.3.2 to 15.0.0.
- [Release notes](https://github.com/Textualize/rich/releases)
- [Changelog](https://github.com/Textualize/rich/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Textualize/rich/compare/v14.3.2...v15.0.0)

---
updated-dependencies:
- dependency-name: rich
  dependency-version: 15.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-16 12:36:42 -06:00
Mike Hunhoff
3cd0e7867b Merge branch 'master' into dependabot/pip/pydantic-2.13.0 2026-04-16 12:36:15 -06:00
Moritz
557f521713 tests: update expected Binary Ninja version to 5.3 (#3011) 2026-04-16 12:35:43 -06:00
Moritz
c0ce1a3fb5 build(deps): bump msgspec from 0.20.0 to 0.21.1 (#3008)
Bumps [msgspec](https://github.com/jcrist/msgspec) from 0.20.0 to 0.21.1.
- [Release notes](https://github.com/jcrist/msgspec/releases)
- [Changelog](https://github.com/jcrist/msgspec/blob/main/docs/changelog.md)
- [Commits](https://github.com/jcrist/msgspec/compare/0.20.0...0.21.1)

---
updated-dependencies:
- dependency-name: msgspec
  dependency-version: 0.21.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-15 07:59:11 +00:00
dependabot[bot]
b4e307b85d build(deps): bump msgspec from 0.20.0 to 0.21.1
Bumps [msgspec](https://github.com/jcrist/msgspec) from 0.20.0 to 0.21.1.
- [Release notes](https://github.com/jcrist/msgspec/releases)
- [Changelog](https://github.com/jcrist/msgspec/blob/main/docs/changelog.md)
- [Commits](https://github.com/jcrist/msgspec/compare/0.20.0...0.21.1)

---
updated-dependencies:
- dependency-name: msgspec
  dependency-version: 0.21.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-13 16:00:22 +00:00
dependabot[bot]
d71dc8520f build(deps): bump pydantic from 2.12.4 to 2.13.0
Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.12.4 to 2.13.0.
- [Release notes](https://github.com/pydantic/pydantic/releases)
- [Changelog](https://github.com/pydantic/pydantic/blob/main/HISTORY.md)
- [Commits](https://github.com/pydantic/pydantic/compare/v2.12.4...v2.13.0)

---
updated-dependencies:
- dependency-name: pydantic
  dependency-version: 2.13.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-13 16:00:05 +00:00
Moritz
99ecd65852 ci: update GitHub Actions to Node.js 24 (#2984)
* ci: update GitHub Actions to Node.js 24 and pin more versions
2026-04-13 16:35:55 +02:00
Mike Hunhoff
0798528b7b ci: use explicit and per job permissions (#3002)
* ci: use explicit and per job permissions

* update CHANGELOG
2026-04-07 14:39:41 -06:00
Mike Hunhoff
c55b06860c ci: fix web rules failure (#3003)
* ci: fix web rules failure

* address feedback

* ruff cleanup
2026-04-07 13:01:23 -06:00
Mike Hunhoff
ed7e0cd77d lint: replace black/isort/flake8 with ruff (#2992)
* lint: replace isort/flake8 with ruff

* update ruff links

* remove stale isort reference

* update CHANGELOG

* address review

* remove unused imports

* remove unnecessary list comprehension

* remove quotes from type annotation

* use dict.get instead of if-else block

* remove unnecessary utf-8 encoding declaration

* Revert "remove unused imports"

This reverts commit 18ba50a22b.

* skip check for unused imports

* fix UP036 Version block is outdated for minimum Python version

* add TODO comment for unused imports

* replace black with ruff

* address review comments
2026-04-07 12:10:41 -06:00
Moritz
ac1cba74b3 feat: update vivisect to 1.3.2 (#3001) 2026-04-07 10:30:21 -06:00
Moritz
ed6b40e967 Merge pull request #3000 from mandiant/dependabot/npm_and_yarn/web/explorer/vite-6.4.2
build(deps-dev): bump vite from 6.4.1 to 6.4.2 in /web/explorer
2026-04-07 09:36:36 +00:00
dependabot[bot]
c07b9e1d33 build(deps-dev): bump vite from 6.4.1 to 6.4.2 in /web/explorer
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.4.1 to 6.4.2.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v6.4.2/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v6.4.2/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 6.4.2
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-06 18:21:14 +00:00
dependabot[bot]
70f275ac0b build(deps-dev): bump types-protobuf (#2994)
Bumps [types-protobuf](https://github.com/python/typeshed) from 6.32.1.20250918 to 7.34.1.20260403.
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-protobuf
  dependency-version: 7.34.1.20260403
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Mike Hunhoff <mike.hunhoff@gmail.com>
2026-04-06 12:15:37 -06:00
dependabot[bot]
63aa5729ee build(deps-dev): bump mypy from 1.19.1 to 1.20.0 (#2993)
Bumps [mypy](https://github.com/python/mypy) from 1.19.1 to 1.20.0.
- [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md)
- [Commits](https://github.com/python/mypy/compare/v1.19.1...v1.20.0)

---
updated-dependencies:
- dependency-name: mypy
  dependency-version: 1.20.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-06 10:42:18 -06:00
dependabot[bot]
63edbedb7c build(deps-dev): bump lodash from 4.17.23 to 4.18.1 in /web/explorer (#2991)
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.23 to 4.18.1.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.23...4.18.1)

---
updated-dependencies:
- dependency-name: lodash
  dependency-version: 4.18.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-06 08:59:48 -06:00
Rizky Mirzaviandy Priambodo
ac82a25e11 build: bump linux standalone build to Python 3.13 (#2941)
Co-authored-by: Mike Hunhoff <mike.hunhoff@gmail.com>
2026-04-03 09:42:00 -06:00
Capa Bot
0b7a5f4b78 Sync capa-testfiles submodule 2026-04-03 15:12:39 +00:00
eversinc33
6aeec0f2b2 Change capa-rules version in installation guide (#2965)
* Change capa-rules version in installation guide

Updated the installation instructions to reflect the newest version of capa-rules.

* add md files from /doc to bumpversion.toml

* adjust rule installation command

* bump to 9.4.0
2026-04-03 09:06:49 -06:00
Moritz
7a79f799a7 Merge pull request #2982 from mandiant/fix/workflow-zip-env
ci: fix ZIP_NAME environment variable in build workflow
v9.4.0
2026-04-01 09:28:48 +00:00
mr-tz
4e4e16391a ci: fix ZIP_NAME environment variable in build workflow 2026-04-01 09:27:00 +00:00
Moritz
3276e351db Prepare release v9.4.0 (#2981)
* Prepare release v9.4.0
2026-04-01 10:58:02 +02:00
Capa Bot
d9b05ed534 Sync capa rules submodule 2026-03-31 16:39:30 +00:00
dependabot[bot]
c5fd75f118 build(deps): bump pyasn1 from 0.5.1 to 0.6.3 (#2939) 2026-03-30 21:12:42 +00:00
Moritz
b82c07d87e Merge pull request #2980 from mandiant/dependabot/pip/pygments-2.20.0 2026-03-30 21:12:06 +00:00
dependabot[bot]
0933594ae9 build(deps): bump pygments from 2.19.1 to 2.20.0
Bumps [pygments](https://github.com/pygments/pygments) from 2.19.1 to 2.20.0.
- [Release notes](https://github.com/pygments/pygments/releases)
- [Changelog](https://github.com/pygments/pygments/blob/master/CHANGES)
- [Commits](https://github.com/pygments/pygments/compare/2.19.1...2.20.0)

---
updated-dependencies:
- dependency-name: pygments
  dependency-version: 2.20.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-30 18:43:24 +00:00
Moritz
db84b2cf33 Merge pull request #2978 from mandiant/dependabot/pip/pygithub-2.9.0
build(deps-dev): bump pygithub from 2.8.1 to 2.9.0
2026-03-30 20:42:45 +02:00
Moritz
693233e9ee Merge pull request #2977 from mandiant/dependabot/pip/types-requests-2.33.0.20260327
build(deps-dev): bump types-requests from 2.32.0.20240712 to 2.33.0.20260327
2026-03-30 20:42:15 +02:00
Moritz
66a26d02ea build(deps): bump pygments from 2.18.0 to 2.20.0 in /web/rules (#2979)
Bumps [pygments](https://github.com/pygments/pygments) from 2.18.0 to 2.20.0.
- [Release notes](https://github.com/pygments/pygments/releases)
- [Changelog](https://github.com/pygments/pygments/blob/master/CHANGES)
- [Commits](https://github.com/pygments/pygments/compare/2.18.0...2.20.0)

---
updated-dependencies:
- dependency-name: pygments
  dependency-version: 2.20.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-30 20:41:47 +02:00
dependabot[bot]
3db27d2e89 build(deps): bump pygments from 2.18.0 to 2.20.0 in /web/rules
Bumps [pygments](https://github.com/pygments/pygments) from 2.18.0 to 2.20.0.
- [Release notes](https://github.com/pygments/pygments/releases)
- [Changelog](https://github.com/pygments/pygments/blob/master/CHANGES)
- [Commits](https://github.com/pygments/pygments/compare/2.18.0...2.20.0)

---
updated-dependencies:
- dependency-name: pygments
  dependency-version: 2.20.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-30 17:41:53 +00:00
dependabot[bot]
e548fa07a4 build(deps-dev): bump pygithub from 2.8.1 to 2.9.0
Bumps [pygithub](https://github.com/pygithub/pygithub) from 2.8.1 to 2.9.0.
- [Release notes](https://github.com/pygithub/pygithub/releases)
- [Changelog](https://github.com/PyGithub/PyGithub/blob/main/doc/changes.rst)
- [Commits](https://github.com/pygithub/pygithub/compare/v2.8.1...v2.9.0)

---
updated-dependencies:
- dependency-name: pygithub
  dependency-version: 2.9.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-30 16:09:57 +00:00
dependabot[bot]
9481499004 build(deps-dev): bump types-requests
Bumps [types-requests](https://github.com/python/typeshed) from 2.32.0.20240712 to 2.33.0.20260327.
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-requests
  dependency-version: 2.33.0.20260327
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-30 16:09:48 +00:00