MALTER is a CLI-first malware analysis pipeline for defensive research and reporting. It does not execute samples on the local host. Static analysis runs locally, and any dynamic execution must be delegated to a CAPEv2 sandbox.
MALTER is the analysis executor and normalizer, and Codex is the default orchestration layer for every analysis run. mal-agent analyze always writes Codex-ready artifacts, especially summary.json, agent_brief.json, next_actions.json, and analysis_runbook.md; there is no separate non-Codex analysis mode.
- Do not execute samples on the local host.
- Dynamic analysis is allowed only through CAPEv2.
- Analyzer failures are recorded per analyzer and should not stop the whole pipeline.
summary.jsonis the source of truth.- LLM or Codex output must not overwrite analyzer findings.
- Static string IOCs remain candidates unless confirmed by stronger evidence.
mal-agent analyzefor sample analysis.mal-agent analyze-dirfor sequential batch analysis.mal-agent servefor the minimal HTTP API.mal-agent workerfor processing durable API jobs separately from the API process.mal-agent reportfor regenerating readable outputs fromsummary.json.mal-agent validate-artifactsfor report quality gating and safe release-bundle regeneration.mal-agent cape-checkfor CAPEv2 connectivity checks.mal-agent tool-checkfor cached local tool and adapter discovery.mal-agent agent-brieffor regenerating mandatory Codex orchestration artifacts fromsummary.json.mal-agent ida-sessionfor Codex-guided iterative IDA MCP follow-up on suspicious imports/functions.mal-agent cape-sessionfor Codex-guided CAPEv2 telemetry interpretation, IOC promotion, and evidence linking.mal-agent codex-orchestratefor running the IDA MCP and CAPEv2 Codex follow-up loops together.mal-agent completion-harnessfor final-design regression checks across tests, safe fixtures, artifacts, docs, and the durable worker queue.- File triage and profile routing for PE, .NET PE, ELF, Mach-O, scripts, documents, archives, APKs, and unknown files.
- Common static analyzers: hash, magic, strings, entropy, IOC candidates, and YARA.
- PE metadata via
pefile, optional LIEF metadata for PE/ELF/Mach-O when available. - .NET CLR metadata parsing, optional ILSpyCmd decompilation, and suspicious API scan.
- PDF/document static metadata, qpdf JSON collection when available, OOXML macro/external relationship hints, OLE directory hints, VBA compressed source recovery, static VBA expression deobfuscation, and suspicious keyword/object hints.
- Script static metadata and suspicious keyword/encoded blob hints.
- APK ZIP metadata, Android binary XML/text manifest parsing, reconstructed manifest XML, resource reference resolution, DEX/native library/certificate listing, and permission extraction.
- ZIP/TAR/GZIP archive listing, optional 7-Zip/py7zr listing, and suspicious entry flags without unrestricted extraction.
- Bounded archive child extraction and recursive static triage.
- IDA MCP direct integration with optional IDA auto-launch, related binary follow-up, and export import through
mal-agent ida-import. - Codex-guided IDA MCP static follow-up and Codex-guided CAPEv2 dynamic follow-up.
- Grounded LLM report generation through local
codex exec, using the normal local Codex login/OAuth state instead of an API key stored by MALTER. - CAPEv2 client and report normalizer.
- Generated YARA syntax/self-match validation and optional benign corpus false-positive checks.
- Correlation records for static/dynamic IOC overlap and related finding context.
- Export writers for
exports/iocs.csv,exports/iocs.stix.json,exports/markdown/report.md, and generated YARA. - Minimal HTTP API backed by a pluggable durable queue and a separate worker command.
- Independent analyzer status values:
ok,skipped,unavailable,error. - Evidence-linked IOC records where possible.
- Codex orchestration artifacts:
agent_brief.json,next_actions.json,analysis_runbook.md.
src/mal_agent/
cmd/ CLI entrypoints
core/ models, config, pipeline, profiles, triage, artifacts
analyzers/ internal analyzers and external tool adapters
generators/ IOC, ATT&CK, report, YARA, Codex orchestration writers
templates/ report templates
docs/
tests/
examples/
packaging/
python -m venv .venv
.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
python -m pip install -e .[dev]The CLI reads mal-agent.toml when present, then overlays supported environment variables.
yara_rule_dirs = ["examples/rules"]
yara_benign_corpus_limit = 0 # 0 = scan the complete configured corpus
capa_path = "capa"
capa_rules_path = ".tools/capa-rules"
capa_signatures_path = ".tools/capa-src/sigs"
# 0 disables a fixed deadline; the no-progress watchdog remains active.
capa_timeout_sec = 0
capa_no_progress_timeout_sec = 7200
ilspycmd_path = "ilspycmd"
qpdf_path = "qpdf"
seven_zip_path = "7z"
dnlib_reader_path = "tools/dnlib_reader/bin/malter-dnlib-reader.exe"
strings_min_length = 6
archive_listing_limit = 0 # 0 = retain the complete listing
archive_extract_children = true
archive_max_depth = 3
archive_max_file_count = 500
archive_max_total_extracted_size = 1073741824
archive_max_compression_ratio = 1000.0
generated_yara_benign_corpus_dir = ""
[quality]
auto_orchestrate = true
max_ida_targets = 0 # 0 = no MALTER-side cap
max_callers_per_import = 0 # 0 = no MALTER-side cap
ida_review_batch_size = 50 # batch/checkpoint size, not a coverage cap
[queue]
backend = "sqlite"
external_base_url = ""
external_token = ""
external_token_env = "MAL_AGENT_QUEUE_TOKEN"
timeout_sec = 30
lease_duration_sec = 180
heartbeat_interval_sec = 20
max_pending_jobs = 1000
max_request_bytes = 262144
request_read_timeout_sec = 30
min_free_disk_bytes = 5368709120
hot_terminal_jobs = 10000
[cape]
base_url = "https://cape.example.org"
token = ""
verify_tls = true
poll_interval_sec = 10
max_wait_sec = 1200
request_full_logs = true
api_cap = 0 # omit CAPE api-cap; CAPE-native limits may still apply
[ida_mcp]
enabled = true
auto_launch = true
ida_path = ""
host = "127.0.0.1"
port = 13337
endpoint_path = "/mcp"
token = ""
launch_args = ["-A"]
launch_timeout_sec = 90
launch_port_search_limit = 20
launch_visible = false
terminate_on_timeout = true
terminate_on_completion = true
analyze_related_binaries = true
auto_launch_related = true
related_max_binaries = 0
related_search_dirs = ["sample_dir"]
related_include_system_dirs = false
related_port_step = 1
load_tool = ""
functions_tool = "list_funcs"
imports_tool = "imports"
xrefs_tool = "xrefs_to"
decompile_tool = "decompile"
max_xref_targets = 0
max_decompile_functions = 0
max_function_records = 0
max_import_records = 0
max_xref_records = 0
max_decompiler_records = 0
[codex_cli]
enabled = true
executable = "codex"
model = "gpt-5.6-sol"
reasoning_effort = "max"
timeout_sec = 0 # no fixed wall-clock deadline
no_progress_timeout_sec = 1800
sandbox = "read-only"
approval_policy = "never"
max_prompt_chars = 0 # no MALTER-side character truncation
max_output_chars = 0 # no MALTER-side character truncation
[llm]
enabled = true
provider = "codex_cli"
input_mode = "full"
max_input_iocs = 0
max_input_evidence = 0
[dotnet_mcp]
enabled = false
endpoint = ""
token = ""
tool_name = "analyze_dotnet"
timeout_sec = 60
use_tools_call = true
[document_mcp]
enabled = false
endpoint = ""
token = ""
tool_name = "analyze_document"
timeout_sec = 60
use_tools_call = true
[script_mcp]
enabled = false
endpoint = ""
token = ""
tool_name = "analyze_script"
timeout_sec = 60
use_tools_call = true
[archive_mcp]
enabled = false
endpoint = ""
token = ""
tool_name = "analyze_archive"
timeout_sec = 60
use_tools_call = true
[apk_mcp]
enabled = false
endpoint = ""
token = ""
tool_name = "analyze_apk"
timeout_sec = 60
use_tools_call = true
[unknown_mcp]
enabled = false
endpoint = ""
token = ""
tool_name = "analyze_unknown"
timeout_sec = 60
use_tools_call = trueEnvironment overrides:
MAL_AGENT_YARA_RULE_DIRSMAL_AGENT_CAPA_PATHMAL_AGENT_CAPA_RULES_PATHMAL_AGENT_CAPA_SIGNATURES_PATHMAL_AGENT_CAPA_TIMEOUT_SECMAL_AGENT_ILSPYCMD_PATHMAL_AGENT_QPDF_PATHMAL_AGENT_SEVEN_ZIP_PATHMAL_AGENT_DNLIB_READER_PATHMAL_AGENT_AUTO_ORCHESTRATEMAL_AGENT_MAX_IDA_TARGETSMAL_AGENT_MAX_CALLERS_PER_IMPORTMAL_AGENT_LLM_INPUT_MODEMAL_AGENT_CODEX_CLI_MODELMAL_AGENT_CODEX_CLI_REASONING_EFFORTMAL_AGENT_CODEX_CLI_TIMEOUT_SECMAL_AGENT_CODEX_CLI_MAX_PROMPT_CHARSMAL_AGENT_CODEX_CLI_MAX_OUTPUT_CHARSMAL_AGENT_STRINGS_MIN_LENGTHMAL_AGENT_ARCHIVE_LISTING_LIMITMAL_AGENT_ARCHIVE_EXTRACT_CHILDRENMAL_AGENT_ARCHIVE_MAX_DEPTHMAL_AGENT_ARCHIVE_MAX_FILE_COUNTMAL_AGENT_ARCHIVE_MAX_TOTAL_EXTRACTED_SIZEMAL_AGENT_YARA_BENIGN_CORPUS_DIRMAL_AGENT_CAPE_BASE_URLMAL_AGENT_CAPE_TOKENMAL_AGENT_CAPE_VERIFY_TLSMAL_AGENT_CAPE_POLL_INTERVAL_SECMAL_AGENT_CAPE_MAX_WAIT_SECMAL_AGENT_IDA_MCP_ENDPOINTMAL_AGENT_IDA_MCP_TOKENMAL_AGENT_IDA_MCP_ENABLEDMAL_AGENT_IDA_PATHMAL_AGENT_IDA_MCP_HOSTMAL_AGENT_IDA_MCP_PORTMAL_AGENT_IDA_MCP_ENDPOINT_PATHMAL_AGENT_IDA_MCP_AUTO_LAUNCHMAL_AGENT_IDA_MCP_LAUNCH_TIMEOUT_SECMAL_AGENT_IDA_MCP_LAUNCH_PORT_SEARCH_LIMITMAL_AGENT_IDA_MCP_LAUNCH_VISIBLEMAL_AGENT_IDA_MCP_TERMINATE_ON_TIMEOUTMAL_AGENT_IDA_MCP_ANALYZE_RELATED_BINARIESMAL_AGENT_IDA_MCP_RELATED_MAX_BINARIESMAL_AGENT_IDA_MCP_RELATED_INCLUDE_SYSTEM_DIRSMAL_AGENT_CODEX_CLI_ENABLEDMAL_AGENT_CODEX_CLI_EXECUTABLEMAL_AGENT_CODEX_CLI_MODELMAL_AGENT_CODEX_CLI_TIMEOUT_SECMAL_AGENT_CODEX_CLI_SANDBOXMAL_AGENT_CODEX_CLI_APPROVAL_POLICYMAL_AGENT_CODEX_CLI_MAX_PROMPT_CHARSMAL_AGENT_CODEX_CLI_MAX_OUTPUT_CHARSMAL_AGENT_LLM_ENABLEDMAL_AGENT_LLM_PROVIDERMAL_AGENT_LLM_MAX_INPUT_IOCSMAL_AGENT_LLM_MAX_INPUT_EVIDENCEMAL_AGENT_DOTNET_MCP_ENABLED,MAL_AGENT_DOTNET_MCP_ENDPOINT,MAL_AGENT_DOTNET_MCP_TOKEN,MAL_AGENT_DOTNET_MCP_TOOL_NAME,MAL_AGENT_DOTNET_MCP_TIMEOUT_SEC,MAL_AGENT_DOTNET_MCP_USE_TOOLS_CALLMAL_AGENT_DOCUMENT_MCP_ENABLED,MAL_AGENT_DOCUMENT_MCP_ENDPOINT,MAL_AGENT_DOCUMENT_MCP_TOKEN,MAL_AGENT_DOCUMENT_MCP_TOOL_NAME,MAL_AGENT_DOCUMENT_MCP_TIMEOUT_SEC,MAL_AGENT_DOCUMENT_MCP_USE_TOOLS_CALLMAL_AGENT_SCRIPT_MCP_ENABLED,MAL_AGENT_SCRIPT_MCP_ENDPOINT,MAL_AGENT_SCRIPT_MCP_TOKEN,MAL_AGENT_SCRIPT_MCP_TOOL_NAME,MAL_AGENT_SCRIPT_MCP_TIMEOUT_SEC,MAL_AGENT_SCRIPT_MCP_USE_TOOLS_CALLMAL_AGENT_ARCHIVE_MCP_ENABLED,MAL_AGENT_ARCHIVE_MCP_ENDPOINT,MAL_AGENT_ARCHIVE_MCP_TOKEN,MAL_AGENT_ARCHIVE_MCP_TOOL_NAME,MAL_AGENT_ARCHIVE_MCP_TIMEOUT_SEC,MAL_AGENT_ARCHIVE_MCP_USE_TOOLS_CALLMAL_AGENT_APK_MCP_ENABLED,MAL_AGENT_APK_MCP_ENDPOINT,MAL_AGENT_APK_MCP_TOKEN,MAL_AGENT_APK_MCP_TOOL_NAME,MAL_AGENT_APK_MCP_TIMEOUT_SEC,MAL_AGENT_APK_MCP_USE_TOOLS_CALLMAL_AGENT_UNKNOWN_MCP_ENABLED,MAL_AGENT_UNKNOWN_MCP_ENDPOINT,MAL_AGENT_UNKNOWN_MCP_TOKEN,MAL_AGENT_UNKNOWN_MCP_TOOL_NAME,MAL_AGENT_UNKNOWN_MCP_TIMEOUT_SEC,MAL_AGENT_UNKNOWN_MCP_USE_TOOLS_CALLMAL_AGENT_QUEUE_BACKENDMAL_AGENT_QUEUE_BASE_URLMAL_AGENT_QUEUE_TOKENMAL_AGENT_QUEUE_TOKEN_ENVMAL_AGENT_QUEUE_TIMEOUT_SEC
Do not hardcode CAPE tokens, profile MCP tokens, API keys, or other secrets in code. codex_cli uses the local codex exec command and the normal local Codex login/OAuth state. LLM report text is generated through codex exec, not by MALTER calling an OpenAI-compatible HTTP API.
Default maximum-quality analysis. This runs local static analyzers, configured deep adapters, CAPEv2 dynamic analysis through the sandbox, Codex-guided IDA/CAPE follow-up loops, and then regenerates the grounded report from the enriched summary.json:
mal-agent analyze .\sample.exeThe quality defaults use gpt-5.6-sol with max reasoning and apply no MALTER-side Codex prompt/output/time cap or IDA target/record cap. 0 means “unlimited by MALTER”; Codex, IDA MCP, CAPEv2, the account, and the operating environment can still enforce native limits. Archive extraction retains path/depth/file-count/total-size safety guards.
Run the baseline pipeline without automatic Codex follow-up loops when explicitly needed:
mal-agent analyze .\sample.exe --no-orchestrateStatic-only analysis, with deep and dynamic stages disabled:
mal-agent analyze .\sample.exe --no-deep --no-dynamicThe CLI prints live pipeline progress at the default INFO log level:
[pipeline] running: file_triage
[pipeline] analyzer dotnet_metadata: skipped
[pipeline] analyzer ida_mcp: unavailable
[pipeline] analyzer cape: error
Use --log-level ERROR when only failures and final exit status should be shown.
Write artifacts to a specific directory:
mal-agent analyze .\sample.exe --output-dir .\artifacts\sample-runResume from an existing summary.json and regenerate final artifacts:
mal-agent analyze .\sample.exe --output-dir .\artifacts\sample-run --resumeFetch CAPEv2 dynamic results from an existing task/report:
mal-agent analyze .\sample.exe --cape-task-id 1234
mal-agent analyze .\sample.exe --cape-report .\cape_report.jsonBatch analyze a directory:
mal-agent analyze-dir .\samples --output-dir .\artifactsanalyze-dir snapshots batch_manifest.json before analysis and excludes its
artifact subtree, so generated reports cannot be ingested as new samples. The
sample directory itself cannot also be the output directory.
Run the HTTP API:
mal-agent serve --host 127.0.0.1 --port 8080 --jobs-dir .\.malter_apiProcess queued API jobs in a separate worker:
mal-agent worker --jobs-dir .\.malter_apiFor single-process local testing only:
mal-agent serve --host 127.0.0.1 --port 8080 --jobs-dir .\.malter_api --embedded-workerCheck CAPEv2 connectivity:
mal-agent cape-checkCache local tool and adapter availability:
mal-agent tool-check --output-dir .malter_toolsRegenerate report artifacts:
mal-agent report .\artifacts\<sha256>\summary.jsonValidate delivery readiness and rebuild the safe report package:
mal-agent validate-artifacts .\artifacts\<sha256>Regenerate Codex orchestration artifacts:
mal-agent agent-brief .\artifacts\<sha256>Run Codex-guided IDA MCP follow-up after baseline analysis. When codex_cli.enabled=true, this invokes local codex exec to prioritize IDA MCP targets before MALTER performs allowlisted xrefs_to, decompile, and disasm calls:
mal-agent ida-session .\artifacts\<sha256>If the artifact's saved IDA MCP endpoint is stale and ida_mcp.auto_launch=true,
ida-session relaunches IDA, refreshes ida_mcp.json, then continues targeted
xref/decompiler review.
Run Codex-guided CAPEv2 follow-up after dynamic analysis. When codex_cli.enabled=true, this invokes local codex exec to review normalized CAPE telemetry and return follow-up priorities as JSON:
mal-agent cape-session .\artifacts\<sha256>cape-session reads summary.json and cape_report.json, creates
analyzer_results/codex_cape_session.json, links CAPE telemetry to evidence IDs,
promotes CAPE-observed IOCs to confirmed, and refreshes summary.json,
iocs.json, report.md, and Codex next actions. If dynamic telemetry is missing
or failed, it submits to CAPEv2 by default; use --no-submit to inspect existing
artifacts only, or --cape-task-id/--cape-report to attach known CAPE results.
Manually rerun both Codex follow-up loops for an existing artifact directory:
mal-agent codex-orchestrate .\artifacts\<sha256>This is the explicit rerun form of the follow-up stage already enabled by a
default analyze. It runs the IDA MCP static follow-up where the profile
supports it and the CAPEv2 dynamic follow-up, refreshes the final LLM report,
and writes analyzer_results/codex_orchestration.json.
Run the final-design completion harness:
mal-agent completion-harness --output-dir .tmp\completion_harnessImport real IDA MCP JSON output:
mal-agent ida-import .\artifacts\<sha256> .\ida_mcp_export.jsonmal-agent analyze <sample> follows this path. Codex orchestration is part of the default pipeline, not an optional mode:
config load
sample validation
hash calculation
artifact directory creation
run_state.json initialization
file triage
AnalyzerProfile selection
common static analyzers
format metadata analyzers and optional external adapters
default CAPEv2 dynamic analysis attempt
post-CAPE static and dynamic capa rule matching
IOC / ATT&CK / Detection / Correlation / Confidence / Recommendation generation
generated YARA creation and validation
summary.json, iocs.json, report.md generation
exports generation
mandatory Codex orchestration artifact generation
Implemented routing:
| File family | Profile | Current behavior |
|---|---|---|
| PE | PeProfile |
PE metadata, optional LIEF, YARA, common static, CAPEv2, then static/dynamic capa |
| .NET PE | DotNetProfile |
CLR metadata parsing, optional dnlib reader, optional ILSpyCmd, PE/common static |
| ELF | ElfProfile |
Magic triage, optional LIEF metadata and hardening interpretation, common static, IDA MCP when configured |
| Mach-O | MachOProfile |
Magic triage, optional LIEF metadata and code-signing/entitlement interpretation, common static, IDA MCP when configured |
| Script | ScriptProfile |
Safe text decoding, suspicious keyword/encoded blob hints, common static |
| PDF/Office/RTF | DocumentProfile |
Safe static keyword/object hints, optional qpdf JSON, OOXML/OLE macro and relationship hints, recovered VBA source, no host opening |
| Archives | ArchiveProfile |
Limited ZIP/TAR/GZIP/7-Zip-style listing, bounded child extraction, recursive static triage |
| APK | ApkProfile |
APK ZIP metadata, text/binary manifest parsing and XML reconstruction, DEX/native library/certificate listing |
| Unknown | UnknownProfile |
Hash, magic, strings, entropy, IOC candidates, YARA |
Each run writes to artifacts/<sha256>/ by default, or to --output-dir when supplied.
Always generated:
summary.json: full normalized source of truthiocs.json: compact IOC projectionreport.md: analyst-readable reportrun_state.json: run statusagent_brief.json: compact Codex-readable briefnext_actions.json: recommended next analysis actionsanalysis_runbook.md: human-readable continuation runbooktool_inventory.json: cached local tool/module/adapter inventoryreport_quality.json: machine-readable delivery quality gate
Codex orchestration markers are also always present:
summary.json.agent_summary.orchestrator = "codex"summary.json.agent_summary.orchestration_mode = "codex_required"summary.json.orchestration_hints.mode = "codex_required"agent_brief.json.orchestration_mode = "codex_required"next_actions.json.orchestration_mode = "codex_required"
Conditionally generated:
generated.yara: conservative draft rule when enough stable strings existcape_report.json: saved when CAPE data is loaded or fetched
Export artifacts:
exports/iocs.csvexports/iocs.stix.jsonexports/markdown/report.mdexports/yara/generated.yarwhen a generated rule existsexports/manifest.jsonexports/release_manifest.jsonexports/malter_report_bundle.zip: safe report bundle that excludes the sample file and raw/extracted/unpacked payloadscompletion_harness.jsonandcompletion_harness.mdwhenmal-agent completion-harnessis run
Analyzer outputs live under analyzer_results/, for example:
analyzer_results/
hash.json
file_triage.json
tool_inventory.json
magic.json
strings.json
entropy.json
ioc_candidates.json
yara.json
common_static.json
pe_metadata.json
lief_metadata.json
dotnet_metadata.json
document_metadata.json
archive_metadata.json
script_metadata.json
apk_metadata.json
capa.json
ida_mcp.json
codex_ida_session.json
yara_validation.json
Every generated report now includes a delivery gate in report_quality.json.
It validates that the human report is consistent with summary.json, the full
iocs.json payload matches, evidence IDs are unique and resolvable, required
artifacts exist, enum values are rendered as analyst-readable text, weak IOC
candidates are downgraded, partial IDA MCP coverage is disclosed, failed dynamic
analysis is visible, generated YARA is marked as draft-only when appropriate,
and safety warnings remain present.
Current summaries use schema 1.1.0 and emit the canonical detections field.
Schema 1.0.0 summaries using detection are migrated on read; newly written
artifacts never emit both keys.
Readiness values:
| Readiness | Meaning |
|---|---|
final_ready |
No blockers or warnings were detected by the delivery gate. |
triage_ready |
The artifact package is coherent, but warnings such as partial IDA coverage, missing analyzer evidence, or draft YARA require analyst follow-up. |
not_ready |
A blocker exists; do not distribute the report until fixed. |
exports/release_manifest.json records file hashes and explicitly marks the
sample as excluded. exports/malter_report_bundle.zip contains only delivery
artifacts such as summary.json, iocs.json, report.md,
report_quality.json, analyzer JSON, IOC exports, and runbook files.
Codex is not a MALTER internal analyzer. It is the default orchestration layer for MALTER artifacts and decides what to do next from the generated JSON/Markdown outputs. A normal analyze run always produces the Codex files below and marks summary.json.orchestration_hints.mode as codex_required.
Primary files for Codex:
agent_brief.jsonnext_actions.jsonanalysis_runbook.mdsummary.jsonanalyzer_results/*.jsoncape_report.jsonwhen present
next_actions.json can recommend actions such as:
run_ida_deep_analysisrun_codex_ida_sessioninspect_ida_functionsinspect_ida_xrefsrun_dotnet_decompileinspect_dotnet_resourcesrun_cape_dynamicinspect_cape_networkinspect_cape_process_treeinspect_pdf_embedded_filesinspect_archive_childrengenerate_final_report
See docs/codex_analysis_agent.md for operating rules.
mal-agent completion-harness is the project-level gate for checking whether the implemented pipeline still matches the final design. It does not execute malware or open active content. It creates benign/synthetic fixtures and runs only static MALTER analysis plus queue processing.
The harness currently checks:
python -m pytestunless--skip-pytestis set- PE, .NET PE, unknown, PDF, OLE/VBA, archive, and APK profile routing
- required artifacts:
summary.json,iocs.json,report.md,run_state.json,agent_brief.json,next_actions.json,analysis_runbook.md - delivery artifacts:
report_quality.json,exports/release_manifest.json, andexports/malter_report_bundle.zip - normalized analyzer statuses
- Codex-ready summary fields
- VBA compressed source recovery into
raw/vba/ - static VBA expression deobfuscation
- APK manifest XML reconstruction/preservation and resource reference resolution
- ELF/Mach-O LIEF interpretation and Mach-O Objective-C/Swift semantic hints
- SQLite durable queue, external HTTP queue adapter, and separate worker processing
- resume regeneration from an existing
summary.json - cached tool inventory creation/reuse
- README/docs blocker scan for open-work markers
Output:
.tmp/completion_harness/
completion_harness.json
completion_harness.md
iteration_1/
fixtures/
artifacts/
jobs/
Dynamic analysis is enabled by default and never runs the sample on the local host. It uses CAPEv2 only, through one of these paths:
- default
mal-agent analyze <sample>submission when CAPE is configured --cape-task-id <id>--cape-report <path-or-url>--no-dynamicdisables the CAPEv2 dynamic stage for that run
Behavior:
--cape-reportloads an existing CAPE JSON report and avoids live submission.--cape-task-idpolls and fetches an existing CAPE task/report.- Default
analyzesubmits the sample to CAPEv2 using the configured API. - CAPE failures are recorded in
dynamic.statusand do not discard static results.
MALTER never falls back to local sample execution.
- Missing
capa: status isunavailable; the run continues. - Missing YARA rules or
yara-python: status isunavailable; the run continues. - Missing LIEF:
lief_metadataisunavailable; PE metadata still usespefile. - Missing dnlib reader: internal CLR metadata still runs;
dnlib_statusisunavailable. - Missing ILSpyCmd: CLR metadata and dnlib reader still run;
ilspycmd_statusisunavailable. - Missing qpdf: document keyword/object hints still run;
qpdf_statusisunavailable. - Missing 7-Zip/py7zr: ZIP/TAR/GZIP still list via Python stdlib; 7z/RAR/ISO listing is
unavailable. - Missing IDA MCP direct access with default deep analysis: analyzer status is
unavailable; import real IDA MCP JSON withmal-agent ida-import. - IDA MCP auto-launch starts IDA for static loading only. The IDA MCP plugin must open the configured MCP tool endpoint, usually
/mcp. If the configured port is busy, MALTER searches for the next free port before launching a new IDA process. If the port does not open beforelaunch_timeout_sec, MALTER records an analyzer error and terminates the IDA process it launched by default. Related binary follow-up defaults to every resolved file in the configured search directories (related_max_binaries = 0), while system directories remain excluded unless explicitly enabled. - Native PE/ELF/Mach-O deep analysis routes to IDA MCP. .NET, document, script, archive, APK, and unknown profiles route to their profile-specific static deep adapter and optional
<family>_mcpendpoint instead of IDA. - Missing profile-specific MCP endpoints do not block analysis; MALTER records the endpoint as unavailable in
analyzer_results/profile_deep.jsonand keeps the profile static fallback summary. - Missing CAPEv2 config with default dynamic analysis: dynamic analysis records an error while static artifacts remain.
- Missing local Codex CLI: LLM report generation and Codex-guided follow-up decisions are skipped or unavailable; analyzer findings remain authoritative.
- Queue workers use owner leases and heartbeats. Only an expired lease can be reclaimed; starting another worker does not requeue live work.
- Equivalent queued, running, or completed requests are idempotent by default. Use
force=trueonly when a genuinely new analysis is intended. - CAPE submission intent and task IDs are persisted in
raw/cape/submission.json. File-creation POST requests are never retried automatically; an ambiguous response requires an explicit--cape-task-idrecovery. - Nested archives share one tree-wide file/byte/compression ledger. Content hashes prevent archive cycles, and unprocessed entries are recorded as
deferredinraw/archive/traversal_ledger.jsoninstead of being silently omitted. - IDA target and decompiler work is written to per-target shards and checkpoints. The configured batch size controls memory and restart granularity, not total coverage.
capaand Codex have no fixed deadline at the quality-first defaults. Their watchdog fires only after output, CPU, and I/O all show no progress for the configured interval.- Admission pauses before the queue/artifact volume crosses the free-disk watermark. Old terminal queue metadata is moved to
jobs_archive.sqlite3rather than discarded.
Implemented:
- Python internal analyzers
- PE metadata
- optional LIEF metadata for PE/ELF/Mach-O with hardening/code-signing interpretation and Mach-O Objective-C/Swift semantic hints
- YARA scanner
- generated YARA validator
- cached tool inventory writer
- capa adapter
- CAPEv2 client/report normalization
- .NET CLR metadata parser, dnlib reader adapter, and optional ILSpyCmd adapter
- PDF keyword/object hints, OOXML parser, OLE/CFB stream parser, VBA source recovery/static expression deobfuscation, and optional qpdf adapter
- script metadata analyzer
- APK ZIP analyzer and Android binary XML/text manifest parser with XML reconstruction and resource reference resolution
- ZIP/TAR/GZIP listing plus optional 7-Zip/py7zr listing
- bounded archive child extraction and recursive static triage
- IDA MCP direct
/mcpadapter, optional IDA auto-launch, uncapped-by-default resolved related-binary follow-up, and export import - live grounded LLM report generation through local
codex execwhen configured - front-loaded deep routing metadata in
analyzer_results/deep_routing.jsonand profile-specificanalyzer_results/profile_deep.json - correlation engine
- export writers
- sequential batch CLI, minimal HTTP API, SQLite durable queue, external HTTP queue adapter, and separate worker CLI
- Codex orchestration artifact writer
Design boundaries:
- VBA analysis uses bounded static expression evaluation and never executes macros.
- APK XML and resource references are resolved from AndroidManifest.xml,
res/values/*.xml, andresources.arscwhen present. - SQLite is the built-in durable queue;
queue.backend=httpconnects the worker/API flow to an external broker service.
python -m pytestSmoke test:
python -m mal_agent.cmd.cli analyze tests\fixtures\samples\minimal_pe.exe --output-dir .tmp\test_artifactsThe test suite uses tiny benign fixtures and does not ship malware.
PyInstaller is the first packaging target:
python -m pip install -e .[build]
pyinstaller packaging\pyinstaller.specdocs/malter_pipeline_final.md: full pipeline design and current implementation statusdocs/codex_analysis_agent.md: Codex orchestration rulesdocs/cape_lab.md: CAPEv2 lab notes, when present