Files
BlackSnufkin fb52b1432e Add Fibratus EDR profile + dashboard cache + GrumpyCats package split
Fibratus EDR profile (kind: fibratus). Pull-from-event-log model, same
shape DetonatorAgent's FibratusEdrPlugin.cs uses: operator configures
Fibratus on the EDR VM with alertsenders.eventlog: {enabled: true,
format: json}; rule matches land in the Application log. Whiskers gains
GET /api/alerts/fibratus/since which wevtutil-queries the log,
extracts <TimeCreated SystemTime> + <EventID> + <Data>, ships the raw
JSON blobs back. The new FibratusEdrAnalyzer mirrors Elastic's
two-phase shape — Phase 1 exec, Phase 2 polls Whiskers — and normalizes
Fibratus's actual schema (events[].proc.{name,exe,cmdline,parent_name,
parent_cmdline,ancestors} + bare tactic.id/technique.id/subtechnique.id
labels) into the saved-view renderer's dict.

Whiskers /api/info now reports telemetry_sources: ['fibratus'] when
fibratus.exe is at C:\Program Files\Fibratus\Bin\, so the
orchestrator can preflight before dispatching. wevtutil's single-quoted
attribute output is parsed correctly.

Dashboard reachability cache (services.edr_health). 30s TTL +
background poller every 15s. Per-probe timeouts dropped 4s/5s -> 2s.
First load post-boot waits at most one probe cycle; every subsequent
load <5ms (cache hit).

GrumpyCats package split: 1085-line monolith into:
  grumpycat.py      — orchestrator (14 lines)
  cli/              — parser, handlers, runner
  litterbox_client/ — base + per-domain mixins (files, analysis,
                       doppelganger, results, edr, reports, system)
                       composed into LitterBoxClient.
LitterBoxMCP.py rewires its one import. New CLI subcommand
fibratus-alerts and matching MCP tool fibratus_alerts_since pull
Fibratus alerts via a LitterBox passthrough endpoint
(/api/edr/fibratus/<profile>/alerts/since) for wire-checking the agent
without dispatching a payload.

CHANGELOG updated.
2026-04-30 05:28:54 -07:00

38 lines
1.4 KiB
Python

"""File-management operations: upload, PID validation, delete."""
from pathlib import Path
from typing import BinaryIO, Dict, Optional, Union
class FilesMixin:
"""Upload a file (or validate / delete one)."""
def upload_file(
self,
file_path: Union[str, Path, BinaryIO],
file_name: Optional[str] = None,
) -> Dict:
"""Upload a file for analysis. Returns the server's `file_info`
block including the canonical MD5 hash you'll feed into other
endpoints.
"""
files = self._prepare_file_upload(file_path, file_name)
try:
response = self._make_request("POST", "/upload", files=files)
return response.json()
finally:
# If we opened the file ourselves (path-based), close it. For
# caller-provided BinaryIO objects, the caller owns the handle.
if isinstance(file_path, (str, Path)):
files["file"][1].close()
def validate_process(self, pid) -> Dict:
"""Validate that a PID exists and is accessible for dynamic analysis."""
response = self._make_request("POST", f"/validate/{pid}")
return response.json()
def delete_file(self, file_hash: str) -> Dict:
"""Delete a file and all of its analysis results."""
response = self._make_request("DELETE", f"/file/{file_hash}")
return response.json()