Repository: mandiant/capa
File: capa/ida/plugin/form.py (Python)

Code before:
            self.edit_rule_path.text(),
            self.edit_rule_author.text(),
            self.edit_rule_scope.currentText(),
            self.edit_analyze.currentIndex(),
        )


class CapaExplorerForm(idaapi.PluginForm):
    """form element for plugin interface"""

    def __init__(self, name: str, option=Options.NO_ANALYSIS):
        """initialize form elements"""
        super().__init__()

        self.form_title: str = name
        self.process_total: int = 0
        self.process_count: int = 0

        self.parent: Any  # QtWidget
        self.ida_hooks: CapaExplorerIdaHooks

        # these are init once objects
        try:
            ida_kernwin.show_wait_box("Performing one-time file analysis")
            self.extractor: CapaExplorerFeatureExtractor = CapaExplorerFeatureExtractor()
            self.feature_cache: CapaRuleGenFeatureCache = CapaRuleGenFeatureCache(self.extractor)
        except Exception as e:
            logger.error("Failed to initialize feature extractor (error: %s)", e, exc_info=True)
            return
        finally:
            ida_kernwin.hide_wait_box()

        # caches used to speed up capa explorer analysis - these must be init to None
        self.resdoc_cache: Optional[capa.render.result_document.ResultDocument] = None
        self.program_analysis_ruleset_cache: Optional[capa.rules.RuleSet] = None
        self.rulegen_ruleset_cache: Optional[capa.rules.RuleSet] = None
        self.rulegen_current_function: Optional[FunctionHandle] = None

        # models
        self.model_data: CapaExplorerDataModel
        self.range_model_proxy: CapaExplorerRangeProxyModel
        self.search_model_proxy: CapaExplorerSearchProxyModel

        # UI controls
        self.view_limit_results_by_function: QtWidgets.QCheckBox
        self.view_show_results_by_function: QtWidgets.QCheckBox
        self.view_search_bar: QtWidgets.QLineEdit
        self.view_tree: CapaExplorerQtreeView
        self.view_tabs: QtWidgets.QTabWidget
        self.view_tab_rulegen = None
        self.view_status_label: QtWidgets.QLabel

Review comment (performance):
Let's move these two initializations to the beginning of `load_capa_function_results`. The rule generator's memory-based cache (`CapaRuleGenCache`) should be initialized once when the user first initiates analysis from the rule generator view (`load_capa_function_results`). This eliminates unnecessary overhead when opening capa explorer if the rule generator is never used.

Currently, we use two different caches: one disk-based cache for the program analysis view and one memory-based cache for the rule generator view. Let's rename `extractor` and `feature_cache` to `rulegen_feature_extractor` and `rulegen_feature_cache`, respectively, so it's easier to track that these two back the rule generator.

Code after:
            self.edit_rule_path.text(),
            self.edit_rule_author.text(),
            self.edit_rule_scope.currentText(),
            self.edit_analyze.currentIndex(),
        )


class CapaExplorerForm(idaapi.PluginForm):
    """form element for plugin interface"""

    def __init__(self, name: str, option=Options.NO_ANALYSIS):
        """initialize form elements"""
        super().__init__()

        self.form_title: str = name
        self.process_total: int = 0
        self.process_count: int = 0

        self.parent: Any  # QtWidget
        self.ida_hooks: CapaExplorerIdaHooks

        # caches used to speed up capa explorer analysis - these must be init to None
        self.resdoc_cache: Optional[capa.render.result_document.ResultDocument] = None
        self.program_analysis_ruleset_cache: Optional[capa.rules.RuleSet] = None
        self.feature_extractor: Optional[CapaExplorerFeatureExtractor] = None
        self.rulegen_feature_extractor: Optional[CapaExplorerFeatureExtractor] = None
        self.rulegen_feature_cache: Optional[CapaRuleGenFeatureCache] = None
        self.rulegen_ruleset_cache: Optional[capa.rules.RuleSet] = None
        self.rulegen_current_function: Optional[FunctionHandle] = None

        # models
        self.model_data: CapaExplorerDataModel
        self.range_model_proxy: CapaExplorerRangeProxyModel
        self.search_model_proxy: CapaExplorerSearchProxyModel

        # UI controls
        self.view_limit_results_by_function: QtWidgets.QCheckBox
        self.view_show_results_by_function: QtWidgets.QCheckBox
        self.view_search_bar: QtWidgets.QLineEdit
        self.view_tree: CapaExplorerQtreeView
        self.view_tabs: QtWidgets.QTabWidget
        self.view_tab_rulegen = None
        self.view_status_label: QtWidgets.QLabel
        self.view_status_label_analysis_cache: str = ""
        self.view_status_label_rulegen_cache: str = ""
        self.view_buttons: QtWidgets.QHBoxLayout
        self.view_analyze_button: QtWidgets.QPushButton
        self.view_reset_button: QtWidgets.QPushButton
        self.view_settings_button: QtWidgets.QPushButton
        self.view_save_button: QtWidgets.QPushButton