Skip to content

Commit

Permalink
Bugfix: Only list discovered components in the GUI (#313)
Browse files Browse the repository at this point in the history
* remove usage of OFRAKEnvironment from GUI server, use standard ComponentLocator filters for component lookups

* remove unused method

* fix tests for get_components, remove explicit IDs from resources which dont need them

---------

Co-authored-by: edward <edward@redballoonsecurity.com>
  • Loading branch information
EdwardLarson and Edward-Larson committed May 23, 2023
1 parent d71059d commit 7bdeeef
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 53 deletions.
2 changes: 0 additions & 2 deletions ofrak_core/ofrak/core/elf/analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ class ElfBasicHeaderAttributesAnalyzer(Analyzer[None, ElfBasicHeader]):
<https://man7.org/linux/man-pages/man5/elf.5.html> for details.
"""

id = b"ElfHeaderMetadataAttributesAnalyzer"
targets = (ElfBasicHeader,)
outputs = (ElfBasicHeader,)

Expand Down Expand Up @@ -218,7 +217,6 @@ class ElfSymbolAttributesAnalyzer(Analyzer[None, ElfSymbol]):
table.
"""

id = b"ElfSymbolAnalyzer"
targets = (ElfSymbol,)
outputs = (ElfSymbol,)

Expand Down
69 changes: 41 additions & 28 deletions ofrak_core/ofrak/gui/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from enum import Enum
import functools
import itertools
import json
import logging

import typing_inspect
Expand Down Expand Up @@ -38,7 +37,14 @@
from dataclasses import fields

from ofrak.component.interface import ComponentInterface
from ofrak.model.component_filters import (
ComponentOrMetaFilter,
ComponentTypeFilter,
ComponentTargetFilter,
ComponentAndMetaFilter,
)
from ofrak.ofrak_context import get_current_ofrak_context
from ofrak.service.component_locator_i import ComponentFilter
from ofrak_patch_maker.toolchain.abstract import Toolchain
from ofrak_type.error import NotFoundError
from ofrak_type.range import Range
Expand Down Expand Up @@ -87,7 +93,6 @@
from ofrak.gui.script_builder import ActionType, ScriptBuilder
from ofrak.service.serialization.pjson_types import PJSONType
from ofrak.core.entropy import DataSummaryAnalyzer
from ofrak.cli.ofrak_cli import OFRAKEnvironment

T = TypeVar("T")
LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -139,7 +144,6 @@ def __init__(
self.resource_view_context: ResourceViewContext = ResourceViewContext()
self.component_context: ComponentContext = ClientComponentContext()
self.script_builder: ScriptBuilder = ScriptBuilder()
self.env = OFRAKEnvironment()
self._app.add_routes(
[
web.post("/create_root_resource", self.create_root_resource),
Expand Down Expand Up @@ -199,6 +203,8 @@ def __init__(
self._job_ids: Dict[str, bytes] = defaultdict(
lambda: ofrak_context.id_service.generate_id()
)
self._all_tags: Dict[str, ResourceTag] = {tag.__name__: tag for tag in ResourceTag.all_tags}

if enable_cors:
try:
import aiohttp_cors # type: ignore
Expand Down Expand Up @@ -686,7 +692,7 @@ async def add_tag(self, request: Request) -> Response:
@exceptions_to_http(SerializedError)
async def get_all_tags(self, request: Request) -> Response:
return json_response(
self._serializer.to_pjson(self._ofrak_context.get_all_tags(), Set[ResourceTag])
self._serializer.to_pjson(set(self._all_tags.values()), Set[ResourceTag])
)

@exceptions_to_http(SerializedError)
Expand Down Expand Up @@ -735,8 +741,10 @@ async def get_components(self, request: Request) -> Response:
async def get_config_for_component(self, request: Request) -> Response:
component_string = request.query.get("component")
if component_string is not None:
component = self.env.components[component_string]
config = self._get_config_for_component(component)
component = self._ofrak_context.component_locator.get_by_id(
component_string.encode("ascii")
)
config = self._get_config_for_component(type(component))
else:
return json_response([])
if (
Expand Down Expand Up @@ -778,7 +786,9 @@ async def run_component(self, request: Request) -> Response:
resource: Resource = await self._get_resource_for_request(request)
component_string = request.query.get("component")
if component_string is not None:
component = self.env.components[component_string]
component = type(
self._ofrak_context.component_locator.get_by_id(component_string.encode("ascii"))
)
config_type = self._get_config_for_component(component)
else:
return json_response([])
Expand Down Expand Up @@ -815,7 +825,7 @@ async def get_tags_and_num_components(self, request: Request):
incl_unpackers = options["unpackers"]
all_resource_tags: Set[Tuple[str, int]] = set()
for specific_tag in resource.get_most_specific_tags():
for tag in inspect.getmro(specific_tag):
for tag in specific_tag.tag_classes():
components = self._get_specific_components(
resource,
only_target,
Expand Down Expand Up @@ -948,29 +958,32 @@ def _get_specific_components(
) -> List[str]:
selected_components = []
tags = resource.get_tags()
if show_all_components and len(set(tags)) == 0:
return []

requested_components = [incl_analyzers, incl_modifiers, incl_packers, incl_unpackers]
all_categories = (Analyzer, Modifier, Packer, Unpacker)
categories: Tuple[Type[ComponentInterface], ...] = (Analyzer, Modifier, Packer, Unpacker)
if any(requested_components):
categories = tuple(itertools.compress(all_categories, requested_components))
else:
categories = all_categories

for component_name, component in self.env.components.items():
if issubclass(component, categories):
if (
# mypy does not see CC.targets as iterable
len([tag for tag in tags if show_all_components or tag in component.targets]) # type: ignore
> 0
):
if (
show_all_components
or target_filter is None
or target_filter in [target.__qualname__ for target in component.targets] # type: ignore
):
# TODO: Get Angr components to work in gui
if "Angr" not in component_name:
selected_components.append(component_name)
categories = tuple(itertools.compress(categories, requested_components))

component_filters: List[ComponentFilter] = [
ComponentOrMetaFilter(*(ComponentTypeFilter(cat) for cat in categories)),
]
if not show_all_components:
component_filters.append(ComponentTargetFilter(*tags))
if target_filter is not None:
component_filters.append(ComponentTargetFilter(self._all_tags[target_filter]))

for component in self._ofrak_context.component_locator.get_components_matching_filter(
ComponentAndMetaFilter(*component_filters)
):
if type(component).__name__ != component.get_id().decode("ascii"):
# TODO: The server lookups for these components won't work yet
continue
if type(component).__name__ == "AngrAnalyzer":
# TODO: The config for this includes some angr types and can't be serialized
continue
selected_components.append(type(component).__name__)

return selected_components

Expand Down
4 changes: 0 additions & 4 deletions ofrak_core/ofrak/ofrak_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,6 @@ async def shutdown_context(self):
await asyncio.gather(*(service.shutdown() for service in self._all_ofrak_services))
logging.shutdown()

def get_all_tags(self) -> Iterable[ResourceTag]:
all_tags = ResourceTag.all_tags
return all_tags


class OFRAK:
DEFAULT_LOG_LEVEL = logging.WARNING
Expand Down
9 changes: 0 additions & 9 deletions ofrak_core/test_ofrak/unit/test_ofrak_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
import pytest

from ofrak import OFRAK, OFRAKContext
from ofrak.core import BasicBlock
from ofrak.core.apk import ApkIdentifier
from ofrak.model.viewable_tag_model import ViewableResourceTag
from ofrak.ofrak_context import get_current_ofrak_context
from ofrak_type.error import NotFoundError, InvalidStateError
from pytest_ofrak import mock_library3
Expand Down Expand Up @@ -85,10 +83,3 @@ async def test_get_ofrak_context_fixture(ofrak_context: OFRAKContext):
current_ofrak_context = get_current_ofrak_context()
assert current_ofrak_context is not None
assert current_ofrak_context is ofrak_context


async def test_get_all_tags(ofrak_context: OFRAKContext):
tags = set(ofrak_context.get_all_tags())
print(tags)
assert BasicBlock in tags
assert ViewableResourceTag not in tags
25 changes: 15 additions & 10 deletions ofrak_core/test_ofrak/unit/test_ofrak_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@

from aiohttp.test_utils import TestClient

from ofrak import Analyzer, Unpacker, Modifier, Packer
from ofrak.core import File
from ofrak.core.entropy import DataSummaryAnalyzer
from ofrak.gui.server import AiohttpOFRAKServer, start_server
from ofrak.cli.ofrak_cli import OFRAKEnvironment
from ofrak.component.identifier import Identifier
from ofrak.model.component_filters import ComponentOrMetaFilter, ComponentTypeFilter
from ofrak.service.serialization.pjson import (
PJSONSerializationService,
)
Expand Down Expand Up @@ -715,7 +715,7 @@ async def test_clear_action_queue(ofrak_client: TestClient, hello_world_elf):
]


async def test_get_components(ofrak_client: TestClient, hello_world_elf):
async def test_get_components(ofrak_client: TestClient, hello_world_elf, ofrak_context):
create_resp = await ofrak_client.post(
"/create_root_resource", params={"name": "hello_world_elf"}, data=hello_world_elf
)
Expand All @@ -732,13 +732,18 @@ async def test_get_components(ofrak_client: TestClient, hello_world_elf):
"unpackers": True,
},
)
components = await resp.json()
env = OFRAKEnvironment()
assert components == [
comp
for (comp, comp_class) in env.components.items()
if not issubclass(comp_class, Identifier) and "Angr" not in comp
]
components = set(await resp.json())
expected_components = ofrak_context.component_locator.get_components_matching_filter(
ComponentOrMetaFilter(
ComponentTypeFilter(Analyzer),
ComponentTypeFilter(Unpacker),
ComponentTypeFilter(Modifier),
ComponentTypeFilter(Packer),
)
)
assert components == {
type(comp).__name__ for comp in expected_components if "Angr" not in type(comp).__name__
}


async def test_get_config(ofrak_client: TestClient, hello_world_elf):
Expand Down

0 comments on commit 7bdeeef

Please sign in to comment.