mirror of
https://github.com/magnus919/agent-skills.git
synced 2026-09-17 06:26:31 +03:00
fix: relocate binary analysis skill
This commit is contained in:
@@ -0,0 +1,440 @@
|
||||
"""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}
|
||||
)
|
||||
Reference in New Issue
Block a user