mirror of
https://github.com/mandiant/capa.git
synced 2025-12-12 23:59:48 -08:00
Compare commits
58 Commits
ci/add-gem
...
update-ida
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
10d3031093 | ||
|
|
e9b02b372a | ||
|
|
503c34b8f9 | ||
|
|
888295b37a | ||
|
|
5f9c908315 | ||
|
|
cb2e2323f9 | ||
|
|
5ea63770ba | ||
|
|
6795813fbe | ||
|
|
ca708ca52e | ||
|
|
68cf74d60c | ||
|
|
5a0c47419f | ||
|
|
4dbdd9dcfa | ||
|
|
82cbfd33db | ||
|
|
5906bb3ecf | ||
|
|
08319f598f | ||
|
|
e6df6ad0cd | ||
|
|
add09df061 | ||
|
|
acb34e88d6 | ||
|
|
0099e75704 | ||
|
|
da0803b671 | ||
|
|
789747282d | ||
|
|
3bc2d9915c | ||
|
|
5974440ab7 | ||
|
|
b9d517a70b | ||
|
|
e5b8788620 | ||
|
|
ec411f1552 | ||
|
|
6871adc9dc | ||
|
|
07880c1418 | ||
|
|
5a6c8ca7c1 | ||
|
|
3bd8371d0c | ||
|
|
d0c87ef32c | ||
|
|
bd2731f87f | ||
|
|
4a167d7188 | ||
|
|
c01bc346fc | ||
|
|
826330f511 | ||
|
|
40e5095577 | ||
|
|
c7eede3c53 | ||
|
|
1a5f50195a | ||
|
|
aafca2e00a | ||
|
|
3a24fabeb6 | ||
|
|
2f81bb79f9 | ||
|
|
fc83b7b0a1 | ||
|
|
d430aea04e | ||
|
|
1eb42599cf | ||
|
|
618ae2111b | ||
|
|
42b6d8106a | ||
|
|
78a020e1ac | ||
|
|
a80f85aab4 | ||
|
|
f94f554d15 | ||
|
|
d456d52e81 | ||
|
|
2a18b08a80 | ||
|
|
dd2e350a1a | ||
|
|
164a7bdfb5 | ||
|
|
d7c896bbc6 | ||
|
|
8185ac4dde | ||
|
|
92a6ddff99 | ||
|
|
af87fae036 | ||
|
|
c774db26f0 |
22
.bumpversion.toml
Normal file
22
.bumpversion.toml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
[tool.bumpversion]
|
||||||
|
current_version = "9.2.1"
|
||||||
|
|
||||||
|
[[tool.bumpversion.files]]
|
||||||
|
filename = "capa/version.py"
|
||||||
|
search = '__version__ = "{current_version}"'
|
||||||
|
replace = '__version__ = "{new_version}"'
|
||||||
|
|
||||||
|
[[tool.bumpversion.files]]
|
||||||
|
filename = "capa/ida/plugin/ida-plugin.json"
|
||||||
|
search = '"version": "{current_version}"'
|
||||||
|
replace = '"version": "{new_version}"'
|
||||||
|
|
||||||
|
[[tool.bumpversion.files]]
|
||||||
|
filename = "capa/ida/plugin/ida-plugin.json"
|
||||||
|
search = '"flare-capa=={current_version}"'
|
||||||
|
replace = '"flare-capa=={new_version}"'
|
||||||
|
|
||||||
|
[[tool.bumpversion.files]]
|
||||||
|
filename = "CHANGELOG.md"
|
||||||
|
search = "v{current_version}...master"
|
||||||
|
replace = "{current_version}...{new_version}"
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -122,6 +122,7 @@ scripts/perf/*.zip
|
|||||||
*/.DS_Store
|
*/.DS_Store
|
||||||
Pipfile
|
Pipfile
|
||||||
Pipfile.lock
|
Pipfile.lock
|
||||||
|
uv.lock
|
||||||
/cache/
|
/cache/
|
||||||
.github/binja/binaryninja
|
.github/binja/binaryninja
|
||||||
.github/binja/download_headless.py
|
.github/binja/download_headless.py
|
||||||
|
|||||||
27
CHANGELOG.md
27
CHANGELOG.md
@@ -7,21 +7,46 @@
|
|||||||
|
|
||||||
### Breaking Changes
|
### Breaking Changes
|
||||||
|
|
||||||
### New Rules (2)
|
### New Rules (21)
|
||||||
|
|
||||||
- anti-analysis/anti-vm/vm-detection/detect-mouse-movement-via-activity-checks-on-windows tevajdr@gmail.com
|
- anti-analysis/anti-vm/vm-detection/detect-mouse-movement-via-activity-checks-on-windows tevajdr@gmail.com
|
||||||
- nursery/create-executable-heap moritz.raabe@mandiant.com
|
- nursery/create-executable-heap moritz.raabe@mandiant.com
|
||||||
|
- anti-analysis/packer/dxpack/packed-with-dxpack jakubjozwiak@google.com
|
||||||
|
- anti-analysis/anti-av/patch-bitdefender-hooking-dll-function jakubjozwiak@google.com
|
||||||
|
- nursery/acquire-load-driver-privileges mehunhoff@google.com
|
||||||
|
- nursery/communicate-using-ftp mehunhoff@google.com
|
||||||
|
- linking/static/eclipse-paho-mqtt-c/linked-against-eclipse-paho-mqtt-c jakubjozwiak@google.com
|
||||||
|
- linking/static/qmqtt/linked-against-qmqtt jakubjozwiak@google.com
|
||||||
|
- anti-analysis/anti-forensic/disable-powershell-transcription jakubjozwiak@google.com
|
||||||
|
- host-interaction/powershell/bypass-powershell-constrained-language-mode-via-getsystemlockdownpolicy-patch jakubjozwiak@google.com
|
||||||
|
- linking/static/grpc/linked-against-grpc jakubjozwiak@google.com
|
||||||
|
- linking/static/hp-socket/linked-against-hp-socket jakubjozwiak@google.com
|
||||||
|
- load-code/execute-jscript-via-vsaengine-in-dotnet jakubjozwiak@google.com
|
||||||
|
- linking/static/funchook/linked-against-funchook jakubjozwiak@google.com
|
||||||
|
- linking/static/plthook/linked-against-plthook jakubjozwiak@google.com
|
||||||
|
- host-interaction/network/enumerate-tcp-connections-via-wmi-com-api jakubjozwiak@google.com
|
||||||
|
- host-interaction/network/routing-table/create-routing-table-entry jakubjozwiak@google.com
|
||||||
|
- host-interaction/network/routing-table/get-routing-table michael.hunhoff@mandiant.com
|
||||||
|
- host-interaction/file-system/use-io_uring-io-interface-on-linux jakubjozwiak@google.com
|
||||||
|
- collection/keylog/log-keystrokes-via-direct-input zeze-zeze
|
||||||
-
|
-
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
|
- binja: fix a crash during feature extraction when the MLIL is unavailable @xusheng6 #2714
|
||||||
|
|
||||||
### capa Explorer Web
|
### capa Explorer Web
|
||||||
|
|
||||||
### capa Explorer IDA Pro plugin
|
### capa Explorer IDA Pro plugin
|
||||||
|
|
||||||
|
- add `ida-plugin.json` for inclusion in the IDA Pro plugin repository @williballenthin
|
||||||
|
- ida plugin: add Qt compatibility layer for PyQt5 and PySide6 support @williballenthin #2707
|
||||||
|
- ida plugin: update ida-settings API @mr-tz #2736
|
||||||
|
|
||||||
### Development
|
### Development
|
||||||
|
|
||||||
- ci: remove redundant "test_run" action from build workflow @mike-hunhoff #2692
|
- ci: remove redundant "test_run" action from build workflow @mike-hunhoff #2692
|
||||||
|
- dev: add bumpmyversion to bump and sync versions across the project @mr-tz
|
||||||
|
|
||||||
### Raw diffs
|
### Raw diffs
|
||||||
- [capa v9.2.1...master](https://github.com/mandiant/capa/compare/v9.2.1...master)
|
- [capa v9.2.1...master](https://github.com/mandiant/capa/compare/v9.2.1...master)
|
||||||
|
|||||||
@@ -315,3 +315,6 @@ If you use Ghidra, then you can use the [capa + Ghidra integration](/capa/ghidra
|
|||||||
|
|
||||||
## capa testfiles
|
## capa testfiles
|
||||||
The [capa-testfiles repository](https://github.com/mandiant/capa-testfiles) contains the data we use to test capa's code and rules
|
The [capa-testfiles repository](https://github.com/mandiant/capa-testfiles) contains the data we use to test capa's code and rules
|
||||||
|
|
||||||
|
## mailing list
|
||||||
|
Subscribe to the FLARE mailing list for community announcements! Email "subscribe" to [flare-external@google.com](mailto:flare-external@google.com?subject=subscribe).
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ from binaryninja import (
|
|||||||
Function,
|
Function,
|
||||||
BinaryView,
|
BinaryView,
|
||||||
SymbolType,
|
SymbolType,
|
||||||
ILException,
|
|
||||||
RegisterValueType,
|
RegisterValueType,
|
||||||
VariableSourceType,
|
VariableSourceType,
|
||||||
LowLevelILOperation,
|
LowLevelILOperation,
|
||||||
@@ -192,9 +191,8 @@ def extract_stackstring(fh: FunctionHandle):
|
|||||||
if bv is None:
|
if bv is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
mlil = func.mlil
|
||||||
mlil = func.mlil
|
if mlil is None:
|
||||||
except ILException:
|
|
||||||
return
|
return
|
||||||
|
|
||||||
for block in mlil.basic_blocks:
|
for block in mlil.basic_blocks:
|
||||||
|
|||||||
@@ -14,9 +14,9 @@
|
|||||||
|
|
||||||
|
|
||||||
import ida_kernwin
|
import ida_kernwin
|
||||||
from PyQt5 import QtCore
|
|
||||||
|
|
||||||
from capa.ida.plugin.error import UserCancelledError
|
from capa.ida.plugin.error import UserCancelledError
|
||||||
|
from capa.ida.plugin.qt_compat import QtCore, Signal
|
||||||
from capa.features.extractors.ida.extractor import IdaFeatureExtractor
|
from capa.features.extractors.ida.extractor import IdaFeatureExtractor
|
||||||
from capa.features.extractors.base_extractor import FunctionHandle
|
from capa.features.extractors.base_extractor import FunctionHandle
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ from capa.features.extractors.base_extractor import FunctionHandle
|
|||||||
class CapaExplorerProgressIndicator(QtCore.QObject):
|
class CapaExplorerProgressIndicator(QtCore.QObject):
|
||||||
"""implement progress signal, used during feature extraction"""
|
"""implement progress signal, used during feature extraction"""
|
||||||
|
|
||||||
progress = QtCore.pyqtSignal(str)
|
progress = Signal(str)
|
||||||
|
|
||||||
def update(self, text):
|
def update(self, text):
|
||||||
"""emit progress update
|
"""emit progress update
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ from pathlib import Path
|
|||||||
import idaapi
|
import idaapi
|
||||||
import ida_kernwin
|
import ida_kernwin
|
||||||
import ida_settings
|
import ida_settings
|
||||||
from PyQt5 import QtGui, QtCore, QtWidgets
|
|
||||||
|
|
||||||
import capa.main
|
import capa.main
|
||||||
import capa.rules
|
import capa.rules
|
||||||
@@ -51,10 +50,10 @@ from capa.ida.plugin.hooks import CapaExplorerIdaHooks
|
|||||||
from capa.ida.plugin.model import CapaExplorerDataModel
|
from capa.ida.plugin.model import CapaExplorerDataModel
|
||||||
from capa.ida.plugin.proxy import CapaExplorerRangeProxyModel, CapaExplorerSearchProxyModel
|
from capa.ida.plugin.proxy import CapaExplorerRangeProxyModel, CapaExplorerSearchProxyModel
|
||||||
from capa.ida.plugin.extractor import CapaExplorerFeatureExtractor
|
from capa.ida.plugin.extractor import CapaExplorerFeatureExtractor
|
||||||
|
from capa.ida.plugin.qt_compat import QtGui, QtCore, QtWidgets
|
||||||
from capa.features.extractors.base_extractor import FunctionHandle
|
from capa.features.extractors.base_extractor import FunctionHandle
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
settings = ida_settings.IDASettings("capa")
|
|
||||||
|
|
||||||
CAPA_SETTINGS_RULE_PATH = "rule_path"
|
CAPA_SETTINGS_RULE_PATH = "rule_path"
|
||||||
CAPA_SETTINGS_RULEGEN_AUTHOR = "rulegen_author"
|
CAPA_SETTINGS_RULEGEN_AUTHOR = "rulegen_author"
|
||||||
@@ -79,6 +78,13 @@ AnalyzeOptionsText = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def get_setting(key: str, default=None):
|
||||||
|
try:
|
||||||
|
return ida_settings.get_current_plugin_setting(key)
|
||||||
|
except KeyError:
|
||||||
|
return default
|
||||||
|
|
||||||
|
|
||||||
def write_file(path: Path, data):
|
def write_file(path: Path, data):
|
||||||
""" """
|
""" """
|
||||||
path.write_bytes(data)
|
path.write_bytes(data)
|
||||||
@@ -107,7 +113,7 @@ class QLineEditClicked(QtWidgets.QLineEdit):
|
|||||||
old = self.text()
|
old = self.text()
|
||||||
new = str(
|
new = str(
|
||||||
QtWidgets.QFileDialog.getExistingDirectory(
|
QtWidgets.QFileDialog.getExistingDirectory(
|
||||||
self.parent(), "Please select a capa rules directory", settings.user.get(CAPA_SETTINGS_RULE_PATH, "")
|
self.parent(), "Please select a capa rules directory", get_setting(CAPA_SETTINGS_RULE_PATH, "")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if new:
|
if new:
|
||||||
@@ -125,8 +131,8 @@ class CapaSettingsInputDialog(QtWidgets.QDialog):
|
|||||||
self.setMinimumWidth(500)
|
self.setMinimumWidth(500)
|
||||||
self.setWindowFlags(self.windowFlags() & ~QtCore.Qt.WindowContextHelpButtonHint)
|
self.setWindowFlags(self.windowFlags() & ~QtCore.Qt.WindowContextHelpButtonHint)
|
||||||
|
|
||||||
self.edit_rule_path = QLineEditClicked(settings.user.get(CAPA_SETTINGS_RULE_PATH, ""))
|
self.edit_rule_path = QLineEditClicked(get_setting(CAPA_SETTINGS_RULE_PATH, ""))
|
||||||
self.edit_rule_author = QtWidgets.QLineEdit(settings.user.get(CAPA_SETTINGS_RULEGEN_AUTHOR, ""))
|
self.edit_rule_author = QtWidgets.QLineEdit(get_setting(CAPA_SETTINGS_RULEGEN_AUTHOR, ""))
|
||||||
self.edit_rule_scope = QtWidgets.QComboBox()
|
self.edit_rule_scope = QtWidgets.QComboBox()
|
||||||
self.edit_rules_link = QtWidgets.QLabel()
|
self.edit_rules_link = QtWidgets.QLabel()
|
||||||
self.edit_analyze = QtWidgets.QComboBox()
|
self.edit_analyze = QtWidgets.QComboBox()
|
||||||
@@ -141,11 +147,11 @@ class CapaSettingsInputDialog(QtWidgets.QDialog):
|
|||||||
|
|
||||||
scopes = ("file", "function", "basic block", "instruction")
|
scopes = ("file", "function", "basic block", "instruction")
|
||||||
self.edit_rule_scope.addItems(scopes)
|
self.edit_rule_scope.addItems(scopes)
|
||||||
self.edit_rule_scope.setCurrentIndex(scopes.index(settings.user.get(CAPA_SETTINGS_RULEGEN_SCOPE, "function")))
|
self.edit_rule_scope.setCurrentIndex(scopes.index(get_setting(CAPA_SETTINGS_RULEGEN_SCOPE, "function")))
|
||||||
|
|
||||||
self.edit_analyze.addItems(AnalyzeOptionsText.values())
|
self.edit_analyze.addItems(AnalyzeOptionsText.values())
|
||||||
# set the default analysis option here
|
# set the default analysis option here
|
||||||
self.edit_analyze.setCurrentIndex(settings.user.get(CAPA_SETTINGS_ANALYZE, Options.NO_ANALYSIS))
|
self.edit_analyze.setCurrentIndex(get_setting(CAPA_SETTINGS_ANALYZE, Options.NO_ANALYSIS))
|
||||||
|
|
||||||
buttons = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel, self)
|
buttons = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel, self)
|
||||||
|
|
||||||
@@ -235,7 +241,7 @@ class CapaExplorerForm(idaapi.PluginForm):
|
|||||||
|
|
||||||
self.Show()
|
self.Show()
|
||||||
|
|
||||||
analyze = settings.user.get(CAPA_SETTINGS_ANALYZE)
|
analyze = get_setting(CAPA_SETTINGS_ANALYZE)
|
||||||
if analyze != Options.NO_ANALYSIS or (option & Options.ANALYZE_AUTO) == Options.ANALYZE_AUTO:
|
if analyze != Options.NO_ANALYSIS or (option & Options.ANALYZE_AUTO) == Options.ANALYZE_AUTO:
|
||||||
self.analyze_program(analyze=analyze)
|
self.analyze_program(analyze=analyze)
|
||||||
|
|
||||||
@@ -581,7 +587,7 @@ class CapaExplorerForm(idaapi.PluginForm):
|
|||||||
|
|
||||||
def ensure_capa_settings_rule_path(self):
|
def ensure_capa_settings_rule_path(self):
|
||||||
try:
|
try:
|
||||||
path: str = settings.user.get(CAPA_SETTINGS_RULE_PATH, "")
|
path: str = get_setting(CAPA_SETTINGS_RULE_PATH, "")
|
||||||
|
|
||||||
# resolve rules directory - check self and settings first, then ask user
|
# resolve rules directory - check self and settings first, then ask user
|
||||||
# pathlib.Path considers "" equivalent to "." so we first check if rule path is an empty string
|
# pathlib.Path considers "" equivalent to "." so we first check if rule path is an empty string
|
||||||
@@ -611,7 +617,7 @@ class CapaExplorerForm(idaapi.PluginForm):
|
|||||||
logger.error("rule path %s does not exist or cannot be accessed", path)
|
logger.error("rule path %s does not exist or cannot be accessed", path)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
settings.user[CAPA_SETTINGS_RULE_PATH] = path
|
ida_settings.set_current_plugin_setting(CAPA_SETTINGS_RULE_PATH, path)
|
||||||
except UserCancelledError:
|
except UserCancelledError:
|
||||||
capa.ida.helpers.inform_user_ida_ui("Analysis requires capa rules")
|
capa.ida.helpers.inform_user_ida_ui("Analysis requires capa rules")
|
||||||
logger.warning(
|
logger.warning(
|
||||||
@@ -635,7 +641,7 @@ class CapaExplorerForm(idaapi.PluginForm):
|
|||||||
if not self.ensure_capa_settings_rule_path():
|
if not self.ensure_capa_settings_rule_path():
|
||||||
return False
|
return False
|
||||||
|
|
||||||
rule_path: Path = Path(settings.user.get(CAPA_SETTINGS_RULE_PATH, ""))
|
rule_path: Path = Path(get_setting(CAPA_SETTINGS_RULE_PATH, ""))
|
||||||
try:
|
try:
|
||||||
|
|
||||||
def on_load_rule(_, i, total):
|
def on_load_rule(_, i, total):
|
||||||
@@ -649,10 +655,10 @@ class CapaExplorerForm(idaapi.PluginForm):
|
|||||||
return None
|
return None
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
capa.ida.helpers.inform_user_ida_ui(
|
capa.ida.helpers.inform_user_ida_ui(
|
||||||
f"Failed to load capa rules from {settings.user[CAPA_SETTINGS_RULE_PATH]}"
|
f"Failed to load capa rules from {get_setting(CAPA_SETTINGS_RULE_PATH)}"
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.error("Failed to load capa rules from %s (error: %s).", settings.user[CAPA_SETTINGS_RULE_PATH], e)
|
logger.error("Failed to load capa rules from %s (error: %s).", get_setting(CAPA_SETTINGS_RULE_PATH), e)
|
||||||
logger.error(
|
logger.error(
|
||||||
"Make sure your file directory contains properly " # noqa: G003 [logging statement uses +]
|
"Make sure your file directory contains properly " # noqa: G003 [logging statement uses +]
|
||||||
+ "formatted capa rules. You can download and extract the official rules from %s. "
|
+ "formatted capa rules. You can download and extract the official rules from %s. "
|
||||||
@@ -661,7 +667,7 @@ class CapaExplorerForm(idaapi.PluginForm):
|
|||||||
CAPA_RULESET_DOC_URL,
|
CAPA_RULESET_DOC_URL,
|
||||||
)
|
)
|
||||||
|
|
||||||
settings.user[CAPA_SETTINGS_RULE_PATH] = ""
|
ida_settings.set_current_plugin_setting(CAPA_SETTINGS_RULE_PATH, "")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def load_capa_results(self, new_analysis, from_cache):
|
def load_capa_results(self, new_analysis, from_cache):
|
||||||
@@ -701,7 +707,7 @@ class CapaExplorerForm(idaapi.PluginForm):
|
|||||||
update_wait_box("verifying cached results")
|
update_wait_box("verifying cached results")
|
||||||
|
|
||||||
count_source_rules = self.program_analysis_ruleset_cache.source_rule_count
|
count_source_rules = self.program_analysis_ruleset_cache.source_rule_count
|
||||||
user_settings = settings.user[CAPA_SETTINGS_RULE_PATH]
|
user_settings = get_setting(CAPA_SETTINGS_RULE_PATH)
|
||||||
view_status_rules: str = f"{user_settings} ({count_source_rules} rules)"
|
view_status_rules: str = f"{user_settings} ({count_source_rules} rules)"
|
||||||
|
|
||||||
# warn user about potentially outdated rules, depending on the use-case this may be expected
|
# warn user about potentially outdated rules, depending on the use-case this may be expected
|
||||||
@@ -775,7 +781,7 @@ class CapaExplorerForm(idaapi.PluginForm):
|
|||||||
update_wait_box("extracting features")
|
update_wait_box("extracting features")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
meta = capa.ida.helpers.collect_metadata([Path(settings.user[CAPA_SETTINGS_RULE_PATH])])
|
meta = capa.ida.helpers.collect_metadata([Path(get_setting(CAPA_SETTINGS_RULE_PATH))])
|
||||||
capabilities = capa.capabilities.common.find_capabilities(
|
capabilities = capa.capabilities.common.find_capabilities(
|
||||||
ruleset, self.feature_extractor, disable_progress=True
|
ruleset, self.feature_extractor, disable_progress=True
|
||||||
)
|
)
|
||||||
@@ -855,7 +861,7 @@ class CapaExplorerForm(idaapi.PluginForm):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception("Failed to save results to database (error: %s)", e)
|
logger.exception("Failed to save results to database (error: %s)", e)
|
||||||
return False
|
return False
|
||||||
user_settings = settings.user[CAPA_SETTINGS_RULE_PATH]
|
user_settings = get_setting(CAPA_SETTINGS_RULE_PATH)
|
||||||
count_source_rules = self.program_analysis_ruleset_cache.source_rule_count
|
count_source_rules = self.program_analysis_ruleset_cache.source_rule_count
|
||||||
new_view_status = f"capa rules: {user_settings} ({count_source_rules} rules)"
|
new_view_status = f"capa rules: {user_settings} ({count_source_rules} rules)"
|
||||||
# regardless of new analysis, render results - e.g. we may only want to render results after checking
|
# regardless of new analysis, render results - e.g. we may only want to render results after checking
|
||||||
@@ -1076,13 +1082,13 @@ class CapaExplorerForm(idaapi.PluginForm):
|
|||||||
# load preview and feature tree
|
# load preview and feature tree
|
||||||
self.view_rulegen_preview.load_preview_meta(
|
self.view_rulegen_preview.load_preview_meta(
|
||||||
self.rulegen_current_function.address if self.rulegen_current_function else None,
|
self.rulegen_current_function.address if self.rulegen_current_function else None,
|
||||||
settings.user.get(CAPA_SETTINGS_RULEGEN_AUTHOR, "<insert_author>"),
|
get_setting(CAPA_SETTINGS_RULEGEN_AUTHOR, "<insert_author>"),
|
||||||
settings.user.get(CAPA_SETTINGS_RULEGEN_SCOPE, "function"),
|
get_setting(CAPA_SETTINGS_RULEGEN_SCOPE, "function"),
|
||||||
)
|
)
|
||||||
|
|
||||||
self.view_rulegen_features.load_features(all_file_features, all_function_features)
|
self.view_rulegen_features.load_features(all_file_features, all_function_features)
|
||||||
|
|
||||||
self.set_view_status_label(f"capa rules: {settings.user[CAPA_SETTINGS_RULE_PATH]}")
|
self.set_view_status_label(f"capa rules: {get_setting(CAPA_SETTINGS_RULE_PATH)}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception("Failed to render views (error: %s)", e)
|
logger.exception("Failed to render views (error: %s)", e)
|
||||||
return False
|
return False
|
||||||
@@ -1303,12 +1309,11 @@ class CapaExplorerForm(idaapi.PluginForm):
|
|||||||
""" """
|
""" """
|
||||||
dialog = CapaSettingsInputDialog("capa explorer settings", parent=self.parent)
|
dialog = CapaSettingsInputDialog("capa explorer settings", parent=self.parent)
|
||||||
if dialog.exec_():
|
if dialog.exec_():
|
||||||
(
|
rule_path, rule_author, rule_scope, analyze = dialog.get_values()
|
||||||
settings.user[CAPA_SETTINGS_RULE_PATH],
|
ida_settings.set_current_plugin_setting(CAPA_SETTINGS_RULE_PATH, rule_path)
|
||||||
settings.user[CAPA_SETTINGS_RULEGEN_AUTHOR],
|
ida_settings.set_current_plugin_setting(CAPA_SETTINGS_RULEGEN_AUTHOR, rule_author)
|
||||||
settings.user[CAPA_SETTINGS_RULEGEN_SCOPE],
|
ida_settings.set_current_plugin_setting(CAPA_SETTINGS_RULEGEN_SCOPE, rule_scope)
|
||||||
settings.user[CAPA_SETTINGS_ANALYZE],
|
ida_settings.set_current_plugin_setting(CAPA_SETTINGS_ANALYZE, analyze)
|
||||||
) = dialog.get_values()
|
|
||||||
|
|
||||||
def save_program_analysis(self):
|
def save_program_analysis(self):
|
||||||
""" """
|
""" """
|
||||||
@@ -1358,7 +1363,7 @@ class CapaExplorerForm(idaapi.PluginForm):
|
|||||||
|
|
||||||
@param state: checked state
|
@param state: checked state
|
||||||
"""
|
"""
|
||||||
if state == QtCore.Qt.Checked:
|
if state:
|
||||||
self.limit_results_to_function(idaapi.get_func(idaapi.get_screen_ea()))
|
self.limit_results_to_function(idaapi.get_func(idaapi.get_screen_ea()))
|
||||||
else:
|
else:
|
||||||
self.range_model_proxy.reset_address_range_filter()
|
self.range_model_proxy.reset_address_range_filter()
|
||||||
@@ -1367,7 +1372,7 @@ class CapaExplorerForm(idaapi.PluginForm):
|
|||||||
|
|
||||||
def slot_checkbox_limit_features_by_ea(self, state):
|
def slot_checkbox_limit_features_by_ea(self, state):
|
||||||
""" """
|
""" """
|
||||||
if state == QtCore.Qt.Checked:
|
if state:
|
||||||
self.view_rulegen_features.filter_items_by_ea(idaapi.get_screen_ea())
|
self.view_rulegen_features.filter_items_by_ea(idaapi.get_screen_ea())
|
||||||
else:
|
else:
|
||||||
self.view_rulegen_features.show_all_items()
|
self.view_rulegen_features.show_all_items()
|
||||||
@@ -1408,7 +1413,7 @@ class CapaExplorerForm(idaapi.PluginForm):
|
|||||||
"""create Qt dialog to ask user for a directory"""
|
"""create Qt dialog to ask user for a directory"""
|
||||||
return str(
|
return str(
|
||||||
QtWidgets.QFileDialog.getExistingDirectory(
|
QtWidgets.QFileDialog.getExistingDirectory(
|
||||||
self.parent, "Please select a capa rules directory", settings.user.get(CAPA_SETTINGS_RULE_PATH, "")
|
self.parent, "Please select a capa rules directory", get_setting(CAPA_SETTINGS_RULE_PATH, "")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1417,7 +1422,7 @@ class CapaExplorerForm(idaapi.PluginForm):
|
|||||||
return QtWidgets.QFileDialog.getSaveFileName(
|
return QtWidgets.QFileDialog.getSaveFileName(
|
||||||
None,
|
None,
|
||||||
"Please select a location to save capa rule file",
|
"Please select a location to save capa rule file",
|
||||||
settings.user.get(CAPA_SETTINGS_RULE_PATH, ""),
|
get_setting(CAPA_SETTINGS_RULE_PATH, ""),
|
||||||
"*.yml",
|
"*.yml",
|
||||||
)[0]
|
)[0]
|
||||||
|
|
||||||
|
|||||||
38
capa/ida/plugin/ida-plugin.json
Normal file
38
capa/ida/plugin/ida-plugin.json
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"IDAMetadataDescriptorVersion": 1,
|
||||||
|
"plugin": {
|
||||||
|
"name": "capa",
|
||||||
|
"entryPoint": "capa_explorer.py",
|
||||||
|
"version": "9.2.1",
|
||||||
|
"idaVersions": ">=7.4",
|
||||||
|
"description": "Identify capabilities in executable files using FLARE's capa framework",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"categories": [
|
||||||
|
"malware-analysis",
|
||||||
|
"api-scripting-and-automation",
|
||||||
|
"ui-ux-and-visualization"
|
||||||
|
],
|
||||||
|
"pythonDependencies": ["flare-capa==9.2.1"],
|
||||||
|
"urls": {
|
||||||
|
"repository": "https://github.com/mandiant/capa"
|
||||||
|
},
|
||||||
|
"authors": [
|
||||||
|
{"name": "Willi Ballenthin", "email": "wballenthin@hex-rays.com"},
|
||||||
|
{"name": "Moritz Raabe", "email": "moritzraabe@google.com"},
|
||||||
|
{"name": "Mike Hunhoff", "email": "mike.hunhoff@gmail.com"},
|
||||||
|
{"name": "Yacine Elhamer", "email": "elhamer.yacine@gmail.com"}
|
||||||
|
],
|
||||||
|
"keywords": [
|
||||||
|
"capability-detection",
|
||||||
|
"malware-analysis",
|
||||||
|
"behavior-analysis",
|
||||||
|
"reverse-engineering",
|
||||||
|
"att&ck",
|
||||||
|
"rule-engine",
|
||||||
|
"feature-extraction",
|
||||||
|
"yara-like-rules",
|
||||||
|
"static-analysis",
|
||||||
|
"dynamic-analysis"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,10 +18,10 @@ from typing import Iterator, Optional
|
|||||||
|
|
||||||
import idc
|
import idc
|
||||||
import idaapi
|
import idaapi
|
||||||
from PyQt5 import QtCore
|
|
||||||
|
|
||||||
import capa.ida.helpers
|
import capa.ida.helpers
|
||||||
from capa.features.address import Address, FileOffsetAddress, AbsoluteVirtualAddress
|
from capa.features.address import Address, FileOffsetAddress, AbsoluteVirtualAddress
|
||||||
|
from capa.ida.plugin.qt_compat import QtCore, qt_get_item_flag_tristate
|
||||||
|
|
||||||
|
|
||||||
def info_to_name(display):
|
def info_to_name(display):
|
||||||
@@ -55,7 +55,7 @@ class CapaExplorerDataItem:
|
|||||||
self.flags = QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
|
self.flags = QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
|
||||||
|
|
||||||
if self._can_check:
|
if self._can_check:
|
||||||
self.flags = self.flags | QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsTristate
|
self.flags = self.flags | QtCore.Qt.ItemIsUserCheckable | qt_get_item_flag_tristate()
|
||||||
|
|
||||||
if self.pred:
|
if self.pred:
|
||||||
self.pred.appendChild(self)
|
self.pred.appendChild(self)
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ from collections import deque
|
|||||||
|
|
||||||
import idc
|
import idc
|
||||||
import idaapi
|
import idaapi
|
||||||
from PyQt5 import QtGui, QtCore
|
|
||||||
|
|
||||||
import capa.rules
|
import capa.rules
|
||||||
import capa.ida.helpers
|
import capa.ida.helpers
|
||||||
@@ -42,6 +41,7 @@ from capa.ida.plugin.item import (
|
|||||||
CapaExplorerInstructionViewItem,
|
CapaExplorerInstructionViewItem,
|
||||||
)
|
)
|
||||||
from capa.features.address import Address, AbsoluteVirtualAddress
|
from capa.features.address import Address, AbsoluteVirtualAddress
|
||||||
|
from capa.ida.plugin.qt_compat import QtGui, QtCore
|
||||||
|
|
||||||
# default highlight color used in IDA window
|
# default highlight color used in IDA window
|
||||||
DEFAULT_HIGHLIGHT = 0xE6C700
|
DEFAULT_HIGHLIGHT = 0xE6C700
|
||||||
@@ -269,7 +269,7 @@ class CapaExplorerDataModel(QtCore.QAbstractItemModel):
|
|||||||
visited.add(child_index)
|
visited.add(child_index)
|
||||||
|
|
||||||
for idx in range(self.rowCount(child_index)):
|
for idx in range(self.rowCount(child_index)):
|
||||||
stack.append(child_index.child(idx, 0))
|
stack.append(self.index(idx, 0, child_index))
|
||||||
|
|
||||||
def reset_ida_highlighting(self, item, checked):
|
def reset_ida_highlighting(self, item, checked):
|
||||||
"""reset IDA highlight for item
|
"""reset IDA highlight for item
|
||||||
|
|||||||
@@ -12,10 +12,8 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from PyQt5 import QtCore
|
|
||||||
from PyQt5.QtCore import Qt
|
|
||||||
|
|
||||||
from capa.ida.plugin.model import CapaExplorerDataModel
|
from capa.ida.plugin.model import CapaExplorerDataModel
|
||||||
|
from capa.ida.plugin.qt_compat import Qt, QtCore
|
||||||
|
|
||||||
|
|
||||||
class CapaExplorerRangeProxyModel(QtCore.QSortFilterProxyModel):
|
class CapaExplorerRangeProxyModel(QtCore.QSortFilterProxyModel):
|
||||||
|
|||||||
79
capa/ida/plugin/qt_compat.py
Normal file
79
capa/ida/plugin/qt_compat.py
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
# Copyright 2020 Google LLC
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Qt compatibility layer for capa IDA Pro plugin.
|
||||||
|
|
||||||
|
Handles PyQt5 (IDA < 9.2) vs PySide6 (IDA >= 9.2) differences.
|
||||||
|
This module provides a unified import interface for Qt modules and handles
|
||||||
|
API changes between Qt5 and Qt6.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
# IDA 9.2+ uses PySide6
|
||||||
|
from PySide6 import QtGui, QtCore, QtWidgets
|
||||||
|
from PySide6.QtGui import QAction
|
||||||
|
|
||||||
|
QT_LIBRARY = "PySide6"
|
||||||
|
Signal = QtCore.Signal
|
||||||
|
except ImportError:
|
||||||
|
# Older IDA versions use PyQt5
|
||||||
|
try:
|
||||||
|
from PyQt5 import QtGui, QtCore, QtWidgets
|
||||||
|
from PyQt5.QtWidgets import QAction
|
||||||
|
|
||||||
|
QT_LIBRARY = "PyQt5"
|
||||||
|
Signal = QtCore.pyqtSignal
|
||||||
|
except ImportError:
|
||||||
|
raise ImportError("Neither PySide6 nor PyQt5 is available. Cannot initialize capa IDA plugin.")
|
||||||
|
|
||||||
|
Qt = QtCore.Qt
|
||||||
|
|
||||||
|
|
||||||
|
def qt_get_item_flag_tristate():
|
||||||
|
"""
|
||||||
|
Get the tristate item flag compatible with Qt5 and Qt6.
|
||||||
|
|
||||||
|
Qt5 (PyQt5): Uses Qt.ItemIsTristate
|
||||||
|
Qt6 (PySide6): Qt.ItemIsTristate was removed, uses Qt.ItemIsAutoTristate
|
||||||
|
|
||||||
|
ItemIsAutoTristate automatically manages tristate based on child checkboxes,
|
||||||
|
matching the original ItemIsTristate behavior where parent checkboxes reflect
|
||||||
|
the check state of their children.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int: The appropriate flag value for the Qt version
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
AttributeError: If the tristate flag cannot be found in the Qt library
|
||||||
|
"""
|
||||||
|
if QT_LIBRARY == "PySide6":
|
||||||
|
# Qt6: ItemIsTristate was removed, replaced with ItemIsAutoTristate
|
||||||
|
# Try different possible locations (API varies slightly across PySide6 versions)
|
||||||
|
if hasattr(Qt, "ItemIsAutoTristate"):
|
||||||
|
return Qt.ItemIsAutoTristate
|
||||||
|
elif hasattr(Qt, "ItemFlag") and hasattr(Qt.ItemFlag, "ItemIsAutoTristate"):
|
||||||
|
return Qt.ItemFlag.ItemIsAutoTristate
|
||||||
|
else:
|
||||||
|
raise AttributeError(
|
||||||
|
"Cannot find ItemIsAutoTristate in PySide6. "
|
||||||
|
+ "Your PySide6 version may be incompatible with capa. "
|
||||||
|
+ f"Available Qt attributes: {[attr for attr in dir(Qt) if 'Item' in attr]}"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# Qt5: Use the original ItemIsTristate flag
|
||||||
|
return Qt.ItemIsTristate
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["qt_get_item_flag_tristate", "Signal", "QAction", "QtGui", "QtCore", "QtWidgets"]
|
||||||
@@ -18,7 +18,6 @@ from collections import Counter
|
|||||||
|
|
||||||
import idc
|
import idc
|
||||||
import idaapi
|
import idaapi
|
||||||
from PyQt5 import QtGui, QtCore, QtWidgets
|
|
||||||
|
|
||||||
import capa.rules
|
import capa.rules
|
||||||
import capa.engine
|
import capa.engine
|
||||||
@@ -28,6 +27,7 @@ import capa.features.basicblock
|
|||||||
from capa.ida.plugin.item import CapaExplorerFunctionItem
|
from capa.ida.plugin.item import CapaExplorerFunctionItem
|
||||||
from capa.features.address import AbsoluteVirtualAddress, _NoAddress
|
from capa.features.address import AbsoluteVirtualAddress, _NoAddress
|
||||||
from capa.ida.plugin.model import CapaExplorerDataModel
|
from capa.ida.plugin.model import CapaExplorerDataModel
|
||||||
|
from capa.ida.plugin.qt_compat import QtGui, QtCore, Signal, QAction, QtWidgets
|
||||||
|
|
||||||
MAX_SECTION_SIZE = 750
|
MAX_SECTION_SIZE = 750
|
||||||
|
|
||||||
@@ -147,7 +147,7 @@ def calc_item_depth(o):
|
|||||||
|
|
||||||
def build_action(o, display, data, slot):
|
def build_action(o, display, data, slot):
|
||||||
""" """
|
""" """
|
||||||
action = QtWidgets.QAction(display, o)
|
action = QAction(display, o)
|
||||||
|
|
||||||
action.setData(data)
|
action.setData(data)
|
||||||
action.triggered.connect(lambda checked: slot(action))
|
action.triggered.connect(lambda checked: slot(action))
|
||||||
@@ -312,7 +312,7 @@ class CapaExplorerRulegenPreview(QtWidgets.QTextEdit):
|
|||||||
|
|
||||||
|
|
||||||
class CapaExplorerRulegenEditor(QtWidgets.QTreeWidget):
|
class CapaExplorerRulegenEditor(QtWidgets.QTreeWidget):
|
||||||
updated = QtCore.pyqtSignal()
|
updated = Signal()
|
||||||
|
|
||||||
def __init__(self, preview, parent=None):
|
def __init__(self, preview, parent=None):
|
||||||
""" """
|
""" """
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
- [ ] Review changes
|
- [ ] Review changes
|
||||||
- capa https://github.com/mandiant/capa/compare/\<last-release\>...master
|
- capa https://github.com/mandiant/capa/compare/\<last-release\>...master
|
||||||
- capa-rules https://github.com/mandiant/capa-rules/compare/\<last-release>\...master
|
- capa-rules https://github.com/mandiant/capa-rules/compare/\<last-release>\...master
|
||||||
|
- [ ] Run `$ bump-my-version bump {patch/minor/major} [--allow-dirty]` to update [capa/version.py](https://github.com/mandiant/capa/blob/master/capa/version.py) and other version files
|
||||||
- [ ] Update [CHANGELOG.md](https://github.com/mandiant/capa/blob/master/CHANGELOG.md)
|
- [ ] Update [CHANGELOG.md](https://github.com/mandiant/capa/blob/master/CHANGELOG.md)
|
||||||
- Do not forget to add a nice introduction thanking contributors
|
- Do not forget to add a nice introduction thanking contributors
|
||||||
- Remember that we need a major release if we introduce breaking changes
|
- Remember that we need a major release if we introduce breaking changes
|
||||||
@@ -36,7 +37,6 @@
|
|||||||
- [capa <release>...master](https://github.com/mandiant/capa/compare/<release>...master)
|
- [capa <release>...master](https://github.com/mandiant/capa/compare/<release>...master)
|
||||||
- [capa-rules <release>...master](https://github.com/mandiant/capa-rules/compare/<release>...master)
|
- [capa-rules <release>...master](https://github.com/mandiant/capa-rules/compare/<release>...master)
|
||||||
```
|
```
|
||||||
- [ ] Update [capa/version.py](https://github.com/mandiant/capa/blob/master/capa/version.py)
|
|
||||||
- [ ] Create a PR with the updated [CHANGELOG.md](https://github.com/mandiant/capa/blob/master/CHANGELOG.md) and [capa/version.py](https://github.com/mandiant/capa/blob/master/capa/version.py). Copy this checklist in the PR description.
|
- [ ] Create a PR with the updated [CHANGELOG.md](https://github.com/mandiant/capa/blob/master/CHANGELOG.md) and [capa/version.py](https://github.com/mandiant/capa/blob/master/capa/version.py). Copy this checklist in the PR description.
|
||||||
- [ ] Update the [homepage](https://github.com/mandiant/capa/blob/master/web/public/index.html) (i.e. What's New section)
|
- [ ] Update the [homepage](https://github.com/mandiant/capa/blob/master/web/public/index.html) (i.e. What's New section)
|
||||||
- [ ] After PR review, merge the PR and [create the release in GH](https://github.com/mandiant/capa/releases/new) using text from the [CHANGELOG.md](https://github.com/mandiant/capa/blob/master/CHANGELOG.md).
|
- [ ] After PR review, merge the PR and [create the release in GH](https://github.com/mandiant/capa/releases/new) using text from the [CHANGELOG.md](https://github.com/mandiant/capa/blob/master/CHANGELOG.md).
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ dependencies = [
|
|||||||
# comments and context.
|
# comments and context.
|
||||||
"pyyaml>=6",
|
"pyyaml>=6",
|
||||||
"colorama>=0.4",
|
"colorama>=0.4",
|
||||||
"ida-settings>=2",
|
"ida-settings>=3.1.0",
|
||||||
"ruamel.yaml>=0.18",
|
"ruamel.yaml>=0.18",
|
||||||
"pefile>=2023.2.7",
|
"pefile>=2023.2.7",
|
||||||
"pyelftools>=0.31",
|
"pyelftools>=0.31",
|
||||||
@@ -104,7 +104,7 @@ dependencies = [
|
|||||||
|
|
||||||
"networkx>=3",
|
"networkx>=3",
|
||||||
|
|
||||||
"dnfile>=0.15.0",
|
"dnfile>=0.17.0",
|
||||||
]
|
]
|
||||||
dynamic = ["version"]
|
dynamic = ["version"]
|
||||||
|
|
||||||
@@ -123,7 +123,7 @@ dev = [
|
|||||||
# and should not conflict with other libraries/tooling.
|
# and should not conflict with other libraries/tooling.
|
||||||
"pre-commit==4.2.0",
|
"pre-commit==4.2.0",
|
||||||
"pytest==8.0.0",
|
"pytest==8.0.0",
|
||||||
"pytest-sugar==1.0.0",
|
"pytest-sugar==1.1.1",
|
||||||
"pytest-instafail==0.5.0",
|
"pytest-instafail==0.5.0",
|
||||||
"flake8==7.3.0",
|
"flake8==7.3.0",
|
||||||
"flake8-bugbear==24.12.12",
|
"flake8-bugbear==24.12.12",
|
||||||
@@ -139,16 +139,17 @@ dev = [
|
|||||||
"ruff==0.12.0",
|
"ruff==0.12.0",
|
||||||
"black==25.1.0",
|
"black==25.1.0",
|
||||||
"isort==6.0.0",
|
"isort==6.0.0",
|
||||||
"mypy==1.16.0",
|
"mypy==1.17.1",
|
||||||
"mypy-protobuf==3.6.0",
|
"mypy-protobuf==3.6.0",
|
||||||
"PyGithub==2.6.0",
|
"PyGithub==2.6.0",
|
||||||
|
"bump-my-version==1.2.4",
|
||||||
# type stubs for mypy
|
# type stubs for mypy
|
||||||
"types-backports==0.1.3",
|
"types-backports==0.1.3",
|
||||||
"types-colorama==0.4.15.11",
|
"types-colorama==0.4.15.11",
|
||||||
"types-PyYAML==6.0.8",
|
"types-PyYAML==6.0.8",
|
||||||
"types-psutil==7.0.0.20250218",
|
"types-psutil==7.0.0.20250218",
|
||||||
"types_requests==2.32.0.20240712",
|
"types_requests==2.32.0.20240712",
|
||||||
"types-protobuf==6.30.2.20250516",
|
"types-protobuf==6.32.1.20250918",
|
||||||
"deptry==0.23.0"
|
"deptry==0.23.0"
|
||||||
]
|
]
|
||||||
build = [
|
build = [
|
||||||
@@ -161,11 +162,13 @@ build = [
|
|||||||
"build==1.2.2"
|
"build==1.2.2"
|
||||||
]
|
]
|
||||||
scripts = [
|
scripts = [
|
||||||
|
# can (optionally) be more lenient on dependencies here
|
||||||
|
# see comment on dependencies for more context
|
||||||
"jschema_to_python==1.2.3",
|
"jschema_to_python==1.2.3",
|
||||||
"psutil==7.0.0",
|
"psutil==7.1.2",
|
||||||
"stix2==3.0.1",
|
"stix2==3.0.1",
|
||||||
"sarif_om==1.0.4",
|
"sarif_om==1.0.4",
|
||||||
"requests==2.32.3",
|
"requests>=2.32.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.deptry]
|
[tool.deptry]
|
||||||
@@ -197,7 +200,8 @@ known_first_party = [
|
|||||||
"idc",
|
"idc",
|
||||||
"java",
|
"java",
|
||||||
"netnode",
|
"netnode",
|
||||||
"PyQt5"
|
"PyQt5",
|
||||||
|
"PySide6"
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.deptry.per_rule_ignores]
|
[tool.deptry.per_rule_ignores]
|
||||||
@@ -205,6 +209,7 @@ known_first_party = [
|
|||||||
DEP002 = [
|
DEP002 = [
|
||||||
"black",
|
"black",
|
||||||
"build",
|
"build",
|
||||||
|
"bump-my-version",
|
||||||
"deptry",
|
"deptry",
|
||||||
"flake8",
|
"flake8",
|
||||||
"flake8-bugbear",
|
"flake8-bugbear",
|
||||||
|
|||||||
@@ -10,18 +10,18 @@ annotated-types==0.7.0
|
|||||||
colorama==0.4.6
|
colorama==0.4.6
|
||||||
cxxfilt==0.3.0
|
cxxfilt==0.3.0
|
||||||
dncil==1.0.2
|
dncil==1.0.2
|
||||||
dnfile==0.15.0
|
dnfile==0.17.0
|
||||||
funcy==2.0
|
funcy==2.0
|
||||||
humanize==4.12.0
|
humanize==4.13.0
|
||||||
ida-netnode==3.0
|
ida-netnode==3.0
|
||||||
ida-settings==2.1.0
|
ida-settings==3.2.2
|
||||||
intervaltree==3.1.0
|
intervaltree==3.1.0
|
||||||
markdown-it-py==3.0.0
|
markdown-it-py==4.0.0
|
||||||
mdurl==0.1.2
|
mdurl==0.1.2
|
||||||
msgpack==1.0.8
|
msgpack==1.0.8
|
||||||
networkx==3.4.2
|
networkx==3.4.2
|
||||||
pefile==2024.8.26
|
pefile==2024.8.26
|
||||||
pip==25.1.1
|
pip==25.3
|
||||||
protobuf==6.31.1
|
protobuf==6.31.1
|
||||||
pyasn1==0.5.1
|
pyasn1==0.5.1
|
||||||
pyasn1-modules==0.3.0
|
pyasn1-modules==0.3.0
|
||||||
@@ -36,12 +36,13 @@ pyelftools==0.32
|
|||||||
pygments==2.19.1
|
pygments==2.19.1
|
||||||
python-flirt==0.9.2
|
python-flirt==0.9.2
|
||||||
pyyaml==6.0.2
|
pyyaml==6.0.2
|
||||||
rich==14.0.0
|
rich==14.2.0
|
||||||
ruamel-yaml==0.18.6
|
ruamel-yaml==0.18.6
|
||||||
ruamel-yaml-clib==0.2.8
|
ruamel-yaml-clib==0.2.14
|
||||||
setuptools==80.9.0
|
setuptools==80.9.0
|
||||||
six==1.17.0
|
six==1.17.0
|
||||||
sortedcontainers==2.4.0
|
sortedcontainers==2.4.0
|
||||||
viv-utils==0.8.0
|
viv-utils==0.8.0
|
||||||
vivisect==1.2.1
|
vivisect==1.2.1
|
||||||
msgspec==0.19.0
|
msgspec==0.19.0
|
||||||
|
bump-my-version==1.2.4
|
||||||
|
|||||||
2
rules
2
rules
Submodule rules updated: 2f09b4d471...9e4cc28265
Submodule tests/data updated: 836bd7acc0...5ea5d9f572
@@ -70,4 +70,4 @@ def test_standalone_binja_backend():
|
|||||||
@pytest.mark.skipif(binja_present is False, reason="Skip binja tests if the binaryninja Python API is not installed")
|
@pytest.mark.skipif(binja_present is False, reason="Skip binja tests if the binaryninja Python API is not installed")
|
||||||
def test_binja_version():
|
def test_binja_version():
|
||||||
version = binaryninja.core_version_info()
|
version = binaryninja.core_version_info()
|
||||||
assert version.major == 5 and version.minor == 0
|
assert version.major == 5 and version.minor == 1
|
||||||
|
|||||||
209
web/explorer/package-lock.json
generated
209
web/explorer/package-lock.json
generated
@@ -27,7 +27,7 @@
|
|||||||
"eslint-plugin-vue": "^9.23.0",
|
"eslint-plugin-vue": "^9.23.0",
|
||||||
"jsdom": "^24.1.0",
|
"jsdom": "^24.1.0",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"vite": "^6.3.4",
|
"vite": "^6.4.1",
|
||||||
"vite-plugin-singlefile": "^2.2.0",
|
"vite-plugin-singlefile": "^2.2.0",
|
||||||
"vitest": "^3.0.9"
|
"vitest": "^3.0.9"
|
||||||
}
|
}
|
||||||
@@ -1416,6 +1416,20 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/call-bind-apply-helpers": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"es-errors": "^1.3.0",
|
||||||
|
"function-bind": "^1.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/callsites": {
|
"node_modules/callsites": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||||
@@ -1646,6 +1660,21 @@
|
|||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dunder-proto": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bind-apply-helpers": "^1.0.1",
|
||||||
|
"es-errors": "^1.3.0",
|
||||||
|
"gopd": "^1.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/eastasianwidth": {
|
"node_modules/eastasianwidth": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
||||||
@@ -1711,6 +1740,26 @@
|
|||||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/es-define-property": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/es-errors": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/es-module-lexer": {
|
"node_modules/es-module-lexer": {
|
||||||
"version": "1.6.0",
|
"version": "1.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz",
|
||||||
@@ -1718,6 +1767,35 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/es-object-atoms": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"es-errors": "^1.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/es-set-tostringtag": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"es-errors": "^1.3.0",
|
||||||
|
"get-intrinsic": "^1.2.6",
|
||||||
|
"has-tostringtag": "^1.0.2",
|
||||||
|
"hasown": "^2.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/esbuild": {
|
"node_modules/esbuild": {
|
||||||
"version": "0.25.1",
|
"version": "0.25.1",
|
||||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.1.tgz",
|
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.1.tgz",
|
||||||
@@ -2108,13 +2186,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/form-data": {
|
"node_modules/form-data": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
|
||||||
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"asynckit": "^0.4.0",
|
"asynckit": "^0.4.0",
|
||||||
"combined-stream": "^1.0.8",
|
"combined-stream": "^1.0.8",
|
||||||
|
"es-set-tostringtag": "^2.1.0",
|
||||||
|
"hasown": "^2.0.2",
|
||||||
"mime-types": "^2.1.12"
|
"mime-types": "^2.1.12"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -2141,6 +2222,55 @@
|
|||||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/function-bind": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/get-intrinsic": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bind-apply-helpers": "^1.0.2",
|
||||||
|
"es-define-property": "^1.0.1",
|
||||||
|
"es-errors": "^1.3.0",
|
||||||
|
"es-object-atoms": "^1.1.1",
|
||||||
|
"function-bind": "^1.1.2",
|
||||||
|
"get-proto": "^1.0.1",
|
||||||
|
"gopd": "^1.2.0",
|
||||||
|
"has-symbols": "^1.1.0",
|
||||||
|
"hasown": "^2.0.2",
|
||||||
|
"math-intrinsics": "^1.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/get-proto": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"dunder-proto": "^1.0.1",
|
||||||
|
"es-object-atoms": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/glob": {
|
"node_modules/glob": {
|
||||||
"version": "10.4.2",
|
"version": "10.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.2.tgz",
|
||||||
@@ -2215,6 +2345,19 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/gopd": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/graphemer": {
|
"node_modules/graphemer": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
|
||||||
@@ -2230,6 +2373,48 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/has-symbols": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/has-tostringtag": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"has-symbols": "^1.0.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/hasown": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"function-bind": "^1.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/highlight.js": {
|
"node_modules/highlight.js": {
|
||||||
"version": "11.9.0",
|
"version": "11.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.9.0.tgz",
|
||||||
@@ -2608,6 +2793,16 @@
|
|||||||
"@jridgewell/sourcemap-codec": "^1.5.0"
|
"@jridgewell/sourcemap-codec": "^1.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/math-intrinsics": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/micromatch": {
|
"node_modules/micromatch": {
|
||||||
"version": "4.0.8",
|
"version": "4.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
|
||||||
@@ -3606,9 +3801,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "6.3.4",
|
"version": "6.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz",
|
||||||
"integrity": "sha512-BiReIiMS2fyFqbqNT/Qqt4CVITDU9M9vE+DKcVAsB+ZV0wvTKd+3hMbkpxz1b+NmEDMegpVbisKiAZOnvO92Sw==",
|
"integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
"eslint-plugin-vue": "^9.23.0",
|
"eslint-plugin-vue": "^9.23.0",
|
||||||
"jsdom": "^24.1.0",
|
"jsdom": "^24.1.0",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"vite": "^6.3.4",
|
"vite": "^6.4.1",
|
||||||
"vite-plugin-singlefile": "^2.2.0",
|
"vite-plugin-singlefile": "^2.2.0",
|
||||||
"vitest": "^3.0.9"
|
"vitest": "^3.0.9"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user