mirror of
https://github.com/magnus919/agent-skills.git
synced 2026-09-11 19:47:12 +03:00
441 lines
10 KiB
Python
441 lines
10 KiB
Python
"""Canonical domain entities as dataclasses.
|
|
|
|
All entities use typed fields with proper defaults. Every entity can be
|
|
serialized to a JSON-compatible dict via asdict() or the schema helpers.
|
|
Address objects use the canonical structured format with space, offset,
|
|
display, and optional file_offset.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass, field
|
|
from typing import Any
|
|
from uuid import UUID, uuid4
|
|
|
|
from binary_analysis.domain.enums import (
|
|
AuditResult,
|
|
Confidence,
|
|
DiagnosticSeverity,
|
|
Endianness,
|
|
FunctionNameSource,
|
|
ImportResolution,
|
|
ProjectState,
|
|
ReferenceKind,
|
|
ReportType,
|
|
)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Canonical Address
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class Address:
|
|
"""Canonical structured address.
|
|
|
|
Attributes:
|
|
space: Address space name (e.g., "ram", "register", "const").
|
|
offset: Hex-prefixed offset string (e.g., "0x4018d0").
|
|
display: Human-readable display form (e.g., "0x4018d0").
|
|
file_offset: Optional byte offset within the file on disk.
|
|
"""
|
|
|
|
space: str
|
|
offset: str
|
|
display: str
|
|
file_offset: int | None = None
|
|
|
|
def __post_init__(self) -> None:
|
|
"""Validate offset format."""
|
|
if not self.offset.startswith("0x"):
|
|
raise ValueError(f"Address offset must start with '0x', got: {self.offset!r}")
|
|
|
|
def to_dict(self) -> dict[str, Any]:
|
|
"""Serialize to a canonical dict for JSON output."""
|
|
result: dict[str, Any] = {
|
|
"space": self.space,
|
|
"offset": self.offset,
|
|
"display": self.display,
|
|
}
|
|
if self.file_offset is not None:
|
|
result["file_offset"] = self.file_offset
|
|
return result
|
|
|
|
@classmethod
|
|
def from_dict(cls, data: dict[str, Any]) -> Address:
|
|
"""Deserialize from a canonical dict."""
|
|
return cls(
|
|
space=data["space"],
|
|
offset=data["offset"],
|
|
display=data["display"],
|
|
file_offset=data.get("file_offset"),
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Domain Entities
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@dataclass
|
|
class Project:
|
|
"""Persistent analysis workspace.
|
|
|
|
Identity: UUID.
|
|
"""
|
|
|
|
id: UUID = field(default_factory=uuid4)
|
|
name: str = ""
|
|
state: ProjectState = ProjectState.CREATED
|
|
created_at: str = ""
|
|
updated_at: str = ""
|
|
workspace_version: str = "1"
|
|
binary_count: int = 0
|
|
is_stale: bool = False
|
|
lock: dict[str, Any] | None = None
|
|
description: str | None = None
|
|
max_binary_size_bytes: int | None = None
|
|
|
|
|
|
@dataclass
|
|
class Binary:
|
|
"""Imported artifact identified by SHA-256.
|
|
|
|
Identity: UUID + SHA-256.
|
|
"""
|
|
|
|
id: UUID = field(default_factory=uuid4)
|
|
sha256: str = ""
|
|
path: str = ""
|
|
format: str = ""
|
|
import_mode: str = "copy"
|
|
size_bytes: int = 0
|
|
architecture: str | None = None
|
|
endianness: Endianness | None = None
|
|
entry_point: Address | None = None
|
|
compiler: str | None = None
|
|
source_language: str | None = None
|
|
imported_at: str | None = None
|
|
analyzed_at: str | None = None
|
|
analysis_profile: str | None = None
|
|
is_stale: bool = False
|
|
|
|
|
|
@dataclass
|
|
class Section:
|
|
"""Mapped code or data region within a binary.
|
|
|
|
Identity: Name + binary ID.
|
|
"""
|
|
|
|
name: str = ""
|
|
binary_id: UUID | None = None
|
|
address: Address | None = None
|
|
virtual_size: int = 0
|
|
raw_size: int = 0
|
|
flags: list[str] = field(default_factory=list)
|
|
entropy: float | None = None
|
|
content_hash: str | None = None
|
|
|
|
|
|
@dataclass
|
|
class EntryPoint:
|
|
"""Process, library, boot, or firmware entry point.
|
|
|
|
Identity: Address within binary.
|
|
"""
|
|
|
|
address: Address | None = None
|
|
kind: str = "unknown"
|
|
confidence: Confidence = Confidence.UNKNOWN
|
|
name: str | None = None
|
|
binary_id: UUID | None = None
|
|
|
|
|
|
@dataclass
|
|
class Import:
|
|
"""External dependency symbol.
|
|
|
|
Identity: Address within binary.
|
|
"""
|
|
|
|
module: str = ""
|
|
symbol: str = ""
|
|
address: Address | None = None
|
|
resolution: ImportResolution = ImportResolution.UNRESOLVED
|
|
ordinal: int | None = None
|
|
binary_id: UUID | None = None
|
|
|
|
|
|
@dataclass
|
|
class Export:
|
|
"""Public symbol or forwarder.
|
|
|
|
Identity: Address or ordinal.
|
|
"""
|
|
|
|
name: str = ""
|
|
address: Address | None = None
|
|
ordinal: int | None = None
|
|
forwarder: str | None = None
|
|
kind: str = "function"
|
|
binary_id: UUID | None = None
|
|
|
|
|
|
@dataclass
|
|
class Symbol:
|
|
"""Named entity with source and scope.
|
|
|
|
Identity: Address within binary.
|
|
"""
|
|
|
|
name: str = ""
|
|
address: Address | None = None
|
|
source: FunctionNameSource = FunctionNameSource.UNKNOWN
|
|
scope: str = "unknown"
|
|
binary_id: UUID | None = None
|
|
|
|
|
|
@dataclass
|
|
class String:
|
|
"""Decoded string at a specific address.
|
|
|
|
Identity: Address + encoding + length.
|
|
"""
|
|
|
|
text: str = ""
|
|
encoding: str = "ASCII"
|
|
address: Address | None = None
|
|
length: int = 0
|
|
binary_id: UUID | None = None
|
|
|
|
|
|
@dataclass
|
|
class Function:
|
|
"""Callable code region.
|
|
|
|
Identity: Binary ID + entry address.
|
|
"""
|
|
|
|
name: str = ""
|
|
address: Address | None = None
|
|
size_bytes: int = 0
|
|
confidence: Confidence = Confidence.UNKNOWN
|
|
name_source: FunctionNameSource = FunctionNameSource.UNKNOWN
|
|
binary_id: UUID | None = None
|
|
is_external: bool = False
|
|
is_thunk: bool = False
|
|
signature: str | None = None
|
|
source_language: str | None = None
|
|
basic_block_count: int | None = None
|
|
instruction_count: int | None = None
|
|
cyclomatic_complexity: int | None = None
|
|
|
|
|
|
@dataclass
|
|
class Instruction:
|
|
"""Canonical machine instruction.
|
|
|
|
Identity: Address within function.
|
|
"""
|
|
|
|
mnemonic: str = ""
|
|
operands: str = ""
|
|
bytes_hex: str = ""
|
|
address: Address | None = None
|
|
size_bytes: int = 0
|
|
function_id: str | None = None
|
|
|
|
|
|
@dataclass
|
|
class BasicBlock:
|
|
"""Control-flow node within a function.
|
|
|
|
Identity: Start address within function.
|
|
"""
|
|
|
|
start_address: Address | None = None
|
|
end_address: Address | None = None
|
|
instruction_count: int = 0
|
|
function_id: str | None = None
|
|
is_entry: bool = False
|
|
is_exit: bool = False
|
|
|
|
|
|
@dataclass
|
|
class Reference:
|
|
"""Directed call, jump, read, write, or data relation.
|
|
|
|
Identity: Address pair + kind.
|
|
"""
|
|
|
|
from_addr: Address | None = None
|
|
to_addr: Address | None = None
|
|
kind: ReferenceKind = ReferenceKind.UNKNOWN
|
|
confidence: Confidence = Confidence.UNKNOWN
|
|
binary_id: UUID | None = None
|
|
|
|
|
|
@dataclass
|
|
class CallGraph:
|
|
"""Bounded call graph rooted at a function.
|
|
|
|
Identity: Derived from function references.
|
|
"""
|
|
|
|
root_address: Address | None = None
|
|
nodes: list[dict[str, Any]] = field(default_factory=list)
|
|
edges: list[dict[str, Any]] = field(default_factory=list)
|
|
max_depth: int = 3
|
|
total_nodes: int = 0
|
|
total_edges: int = 0
|
|
truncated: bool = False
|
|
binary_id: UUID | None = None
|
|
|
|
|
|
@dataclass
|
|
class Diagnostic:
|
|
"""Warning or limitation from an analysis run.
|
|
|
|
Identity: Unique within analysis run.
|
|
"""
|
|
|
|
severity: DiagnosticSeverity = DiagnosticSeverity.INFO
|
|
category: str = ""
|
|
message: str = ""
|
|
component: str | None = None
|
|
remediation: str | None = None
|
|
recoverable: bool = True
|
|
|
|
|
|
@dataclass
|
|
class Capability:
|
|
"""Rule-derived functional indicator.
|
|
|
|
Identity: Name within binary.
|
|
"""
|
|
|
|
name: str = ""
|
|
confidence: Confidence = Confidence.UNKNOWN
|
|
evidence: list[dict[str, Any]] = field(default_factory=list)
|
|
binary_id: UUID | None = None
|
|
|
|
|
|
@dataclass
|
|
class Observation:
|
|
"""Direct deterministic fact from analysis.
|
|
|
|
Identity: Unique within analysis run.
|
|
"""
|
|
|
|
category: str = ""
|
|
description: str = ""
|
|
source: str = ""
|
|
address: Address | None = None
|
|
evidence: Any | None = None
|
|
binary_id: UUID | None = None
|
|
|
|
|
|
@dataclass
|
|
class Heuristic:
|
|
"""Rule-derived interpretation with confidence.
|
|
|
|
Identity: Name within analysis run.
|
|
"""
|
|
|
|
name: str = ""
|
|
description: str = ""
|
|
confidence: Confidence = Confidence.UNKNOWN
|
|
rule_id: str | None = None
|
|
evidence: list[dict[str, Any]] = field(default_factory=list)
|
|
binary_id: UUID | None = None
|
|
|
|
|
|
@dataclass
|
|
class Inference:
|
|
"""Agent-generated interpretation.
|
|
|
|
Identity: Unique within analysis run.
|
|
"""
|
|
|
|
description: str = ""
|
|
confidence: Confidence = Confidence.UNKNOWN
|
|
basis: list[str] = field(default_factory=list)
|
|
binary_id: UUID | None = None
|
|
|
|
|
|
@dataclass
|
|
class Unknown:
|
|
"""Explicit unresolved question.
|
|
|
|
Identity: Address within binary.
|
|
"""
|
|
|
|
address: Address | None = None
|
|
question: str = ""
|
|
category: str | None = None
|
|
binary_id: UUID | None = None
|
|
|
|
|
|
@dataclass
|
|
class Report:
|
|
"""Durable handoff artifact.
|
|
|
|
Identity: UUID.
|
|
"""
|
|
|
|
id: UUID = field(default_factory=uuid4)
|
|
report_type: ReportType = ReportType.TRIAGE
|
|
project_id: UUID | None = None
|
|
binary_id: UUID | None = None
|
|
created_at: str = ""
|
|
format: str = "json"
|
|
summary: str | None = None
|
|
sections: list[dict[str, Any]] = field(default_factory=list)
|
|
|
|
|
|
@dataclass
|
|
class AuditEvent:
|
|
"""Append-only provenance event.
|
|
|
|
Identity: Timestamp sequence.
|
|
"""
|
|
|
|
timestamp: str = ""
|
|
event_type: str = ""
|
|
result: AuditResult = AuditResult.SUCCESS
|
|
project_id: UUID | None = None
|
|
binary_id: UUID | None = None
|
|
user: str | None = None
|
|
details: dict[str, Any] = field(default_factory=dict)
|
|
|
|
|
|
@dataclass
|
|
class TriageResult:
|
|
"""Aggregate result of a triage analysis.
|
|
|
|
Contains observations (facts), heuristics (interpretations),
|
|
unknowns (open questions), and engine diagnostics.
|
|
"""
|
|
|
|
observations: list[Observation] = field(default_factory=list)
|
|
heuristics: list[Heuristic] = field(default_factory=list)
|
|
unknowns: list[Unknown] = field(default_factory=list)
|
|
engine_diagnostics: list[dict[str, Any]] = field(default_factory=list)
|
|
partial: bool = False
|
|
|
|
|
|
@dataclass
|
|
class DiagnosticsResult:
|
|
"""Cumulative diagnostics across project lifecycle.
|
|
|
|
Contains all persistent diagnostics from analysis, triage,
|
|
and other commands, plus any current engine diagnostics.
|
|
"""
|
|
|
|
diagnostics: list[dict[str, Any]] = field(default_factory=list)
|
|
total: int = 0
|
|
by_severity: dict[str, int] = field(
|
|
default_factory=lambda: {"INFO": 0, "WARNING": 0, "ERROR": 0}
|
|
)
|