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

Code before:
                if func_ea is None:
                    # rule match address is not located in a defined function
                    continue

                func_address: AbsoluteVirtualAddress = AbsoluteVirtualAddress(func_ea)
                if not matches_by_function.get(func_address, ()):
                    # create a new function root to nest its rule matches; Note: we must use the address of the
                    # function here so everything is displayed properly
                    matches_by_function[func_address] = (
                        CapaExplorerFunctionItem(self.root_node, func_address, can_check=False),
                        set(),
                    )

                func_root, func_match_cache = matches_by_function[func_address]
                if rule.meta.name in func_match_cache:
                    # only nest each rule once, so if found, skip
                    continue

                # add matched rule to its function cache; create a new rule node whose parent is the matched
                # function node
                func_match_cache.add(rule.meta.name)
                CapaExplorerRuleItem(
                    func_root,
                    rule.meta.name,
                    rule.meta.namespace or "",
                    len(rule.matches),
                    rule.source,
                    can_check=False,
                )

    def render_capa_doc_by_program(self, doc: rd.ResultDocument):
        """ """
        for rule in rutils.capability_rules(doc):
            rule_name = rule.meta.name
            rule_namespace = rule.meta.namespace or ""
            parent = CapaExplorerRuleItem(self.root_node, rule_name, rule_namespace, len(rule.matches), rule.source)

            for (location_, match) in rule.matches:
                location = location_.to_capa()

                parent2: CapaExplorerDataItem
                if rule.meta.scope == capa.rules.FILE_SCOPE:
                    parent2 = parent
                elif rule.meta.scope == capa.rules.FUNCTION_SCOPE:
                    parent2 = CapaExplorerFunctionItem(parent, location)
                elif rule.meta.scope == capa.rules.BASIC_BLOCK_SCOPE:
                    parent2 = CapaExplorerBlockItem(parent, location)
                elif rule.meta.scope == capa.rules.INSTRUCTION_SCOPE:
                    parent2 = CapaExplorerInstructionItem(parent, location)
                else:
                    raise RuntimeError("unexpected rule scope: " + str(rule.meta.scope))

Review comment (suggestion):
don't use the length here (since it can be a bit misleading, see example screenshot), ideally we get the count of matches for this function only

![2023-02-01_14-46-31_ida64](https://user-images.githubusercontent.com/17606537/216060361-7f888fe8-a936-4cf2-a96e-21604055130f.png)


Code after:
            for ea in match_eas:
                func_ea: Optional[int] = capa.ida.helpers.get_func_start_ea(ea)
                if func_ea is None:
                    # rule match address is not located in a defined function
                    continue

                func_address: AbsoluteVirtualAddress = AbsoluteVirtualAddress(func_ea)
                if not matches_by_function.get(func_address, ()):
                    # create a new function root to nest its rule matches; Note: we must use the address of the
                    # function here so everything is displayed properly
                    matches_by_function[func_address] = (
                        CapaExplorerFunctionItem(self.root_node, func_address, can_check=False),
                        set(),
                    )

                func_root, func_match_cache = matches_by_function[func_address]
                if rule.meta.name in func_match_cache:
                    # only nest each rule once, so if found, skip
                    continue

                # add matched rule to its function cache; create a new rule node whose parent is the matched
                # function node
                func_match_cache.add(rule.meta.name)
                CapaExplorerRuleItem(
                    func_root,
                    rule.meta.name,
                    rule.meta.namespace or "",
                    len([ea for ea in match_eas if capa.ida.helpers.get_func_start_ea(ea) == func_ea]),
                    rule.source,
                    can_check=False,
                )

    def render_capa_doc_by_program(self, doc: rd.ResultDocument):
        """ """
        for rule in rutils.capability_rules(doc):
            rule_name = rule.meta.name
            rule_namespace = rule.meta.namespace or ""
            parent = CapaExplorerRuleItem(self.root_node, rule_name, rule_namespace, len(rule.matches), rule.source)

            for (location_, match) in rule.matches:
                location = location_.to_capa()

                parent2: CapaExplorerDataItem
                if rule.meta.scope == capa.rules.FILE_SCOPE:
                    parent2 = parent
                elif rule.meta.scope == capa.rules.FUNCTION_SCOPE:
                    parent2 = CapaExplorerFunctionItem(parent, location)
                elif rule.meta.scope == capa.rules.BASIC_BLOCK_SCOPE:
                    parent2 = CapaExplorerBlockItem(parent, location)
                elif rule.meta.scope == capa.rules.INSTRUCTION_SCOPE:
                    parent2 = CapaExplorerInstructionItem(parent, location)