mirror of
https://github.com/magnus919/agent-skills.git
synced 2026-09-12 12:06:29 +03:00
fix: relocate binary analysis skill
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
"""Report generation — Markdown, JSON, HTML, PDF.
|
||||
|
||||
Provides authoritative Markdown and JSON report generation alongside optional
|
||||
HTML and PDF renderings. Every report includes methodology and provenance
|
||||
sections per the validation contract.
|
||||
|
||||
Also provides the audit event system for append-only, atomic event logging
|
||||
to events.jsonl.
|
||||
"""
|
||||
|
||||
from binary_analysis.reporting.audit import (
|
||||
audit_file_exists,
|
||||
clear_audit,
|
||||
read_audit_events,
|
||||
write_audit_event,
|
||||
)
|
||||
from binary_analysis.reporting.generator import (
|
||||
build_methodology,
|
||||
build_provenance,
|
||||
collect_focused_data,
|
||||
collect_project_data,
|
||||
collect_triage_data,
|
||||
generate_html_report,
|
||||
generate_json_report,
|
||||
generate_markdown_report,
|
||||
generate_pdf_report,
|
||||
write_report,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"audit_file_exists",
|
||||
"build_methodology",
|
||||
"build_provenance",
|
||||
"clear_audit",
|
||||
"collect_focused_data",
|
||||
"collect_project_data",
|
||||
"collect_triage_data",
|
||||
"generate_html_report",
|
||||
"generate_json_report",
|
||||
"generate_markdown_report",
|
||||
"generate_pdf_report",
|
||||
"read_audit_events",
|
||||
"write_audit_event",
|
||||
"write_report",
|
||||
]
|
||||
@@ -0,0 +1,155 @@
|
||||
"""Audit event persistence — append-only events.jsonl.
|
||||
|
||||
Provides atomic audit event writing and reading for the project audit log.
|
||||
Each event is a single-line JSON object appended atomically. Events are
|
||||
immutable and append-only; no modification or deletion is supported.
|
||||
|
||||
Events include: timestamp, command, args, result (AuditResult enum),
|
||||
duration_ms, project_id, and optional details.
|
||||
|
||||
Key guarantees:
|
||||
- Atomic append: no partial lines, no interleaving.
|
||||
- Every line is valid JSON (single-line object).
|
||||
- Events ordered by timestamp (ISO 8601 with timezone).
|
||||
- File only grows; never shrinks or overwrites existing entries.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
from datetime import datetime, timezone
|
||||
from typing import Any
|
||||
|
||||
from binary_analysis.domain.enums import AuditResult
|
||||
from binary_analysis.projects.atomic import atomic_append_text
|
||||
|
||||
AUDIT_FILENAME = "events.jsonl"
|
||||
|
||||
|
||||
def _audit_path(project_path: str) -> str:
|
||||
"""Return the path to the audit events file within a project workspace.
|
||||
|
||||
Args:
|
||||
project_path: Absolute path to the project workspace directory.
|
||||
|
||||
Returns:
|
||||
Full path to the events.jsonl file.
|
||||
"""
|
||||
return os.path.join(project_path, "audit", AUDIT_FILENAME)
|
||||
|
||||
|
||||
def write_audit_event(
|
||||
project_path: str,
|
||||
command: str,
|
||||
result: AuditResult,
|
||||
duration_ms: int,
|
||||
*,
|
||||
args: dict[str, Any] | None = None,
|
||||
project_id: str | None = None,
|
||||
binary_id: str | None = None,
|
||||
details: dict[str, Any] | None = None,
|
||||
) -> None:
|
||||
"""Atomically append a single audit event to events.jsonl.
|
||||
|
||||
Each event is written as a single JSON line. Uses atomic_append_text
|
||||
to guarantee no partial lines or interleaving.
|
||||
|
||||
Args:
|
||||
project_path: Absolute path to the project workspace directory.
|
||||
command: The command name (e.g., "project create", "import", "analyze").
|
||||
result: Outcome from AuditResult enum.
|
||||
duration_ms: Wall-clock duration in milliseconds.
|
||||
args: Non-sensitive command arguments (flags, selectors).
|
||||
project_id: Optional project UUID.
|
||||
binary_id: Optional binary UUID.
|
||||
details: Optional additional event details.
|
||||
"""
|
||||
timestamp = datetime.now(timezone.utc).isoformat()
|
||||
|
||||
event: dict[str, Any] = {
|
||||
"timestamp": timestamp,
|
||||
"command": command,
|
||||
"args": args if args is not None else {},
|
||||
"result": result.value,
|
||||
"duration_ms": duration_ms,
|
||||
}
|
||||
|
||||
if project_id is not None:
|
||||
event["project_id"] = project_id
|
||||
if binary_id is not None:
|
||||
event["binary_id"] = binary_id
|
||||
if details is not None:
|
||||
event["details"] = details
|
||||
|
||||
line = json.dumps(event, ensure_ascii=False)
|
||||
path = _audit_path(project_path)
|
||||
atomic_append_text(path, line)
|
||||
|
||||
|
||||
def read_audit_events(project_path: str) -> list[dict[str, Any]]:
|
||||
"""Read all audit events from events.jsonl, ordered by appearance.
|
||||
|
||||
Events are returned in file order (oldest first), which corresponds to
|
||||
timestamp order since events are appended chronologically.
|
||||
|
||||
Args:
|
||||
project_path: Absolute path to the project workspace directory.
|
||||
|
||||
Returns:
|
||||
List of audit event dicts ordered by timestamp. Empty list if the
|
||||
file does not exist or is empty.
|
||||
"""
|
||||
path = _audit_path(project_path)
|
||||
if not os.path.exists(path):
|
||||
return []
|
||||
|
||||
events: list[dict[str, Any]] = []
|
||||
try:
|
||||
with open(path, encoding="utf-8") as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
try:
|
||||
event = json.loads(line)
|
||||
events.append(event)
|
||||
except json.JSONDecodeError:
|
||||
# Skip corrupted lines but emit a placeholder
|
||||
events.append(
|
||||
{
|
||||
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||
"command": "unknown",
|
||||
"args": {},
|
||||
"result": AuditResult.FAILED.value,
|
||||
"duration_ms": 0,
|
||||
"details": {"error": f"Corrupted audit event: {line[:100]}"},
|
||||
}
|
||||
)
|
||||
except OSError:
|
||||
return []
|
||||
|
||||
return events
|
||||
|
||||
|
||||
def clear_audit(project_path: str) -> None:
|
||||
"""Remove the audit events file (e.g., on project clean).
|
||||
|
||||
Args:
|
||||
project_path: Absolute path to the project workspace directory.
|
||||
"""
|
||||
path = _audit_path(project_path)
|
||||
if os.path.exists(path):
|
||||
os.unlink(path)
|
||||
|
||||
|
||||
def audit_file_exists(project_path: str) -> bool:
|
||||
"""Check if the audit events file exists.
|
||||
|
||||
Args:
|
||||
project_path: Absolute path to the project workspace directory.
|
||||
|
||||
Returns:
|
||||
True if the events.jsonl file exists.
|
||||
"""
|
||||
return os.path.exists(_audit_path(project_path))
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user