This document describes how to report security issues in mt5-quant, and the
secure-development practices the project follows. It backs the OpenSSF Best
Practices criteria vulnerability_report_process, know_secure_design, and
know_common_errors, and the signed-release process described under
Signing backs delivery_unsigned.
mt5-quant is a Rust MCP server that drives MetaTrader 5 (under Wine/CrossOver) to compile, backtest, and analyze trading Expert Advisors. It runs locally; it never exposes a network listener and never transmits user data off the machine except for the opt-in version self-check (HTTPS to GitHub). The security notes below focus on the real risks in that model: untrusted input from MT5 report files, the user-supplied Wine/MT5 configuration, and the build/publish pipeline.
We encourage responsible disclosure. If you discover a vulnerability, please report it through one of the following channels (in order of preference):
- GitHub Security Advisories (recommended). Open a private security advisory at https://github.com/masdevid/mt5-quant/security/advisories/new. This keeps the details confidential while we prepare a fix.
- Public GitHub Issue. If you prefer, open a normal issue at https://github.com/masdevid/mt5-quant/issues. Public reports are accepted; please avoid posting working exploits in the open issue body — link to a private gist or email the maintainer instead.
A useful report contains:
- A clear description of the vulnerability and its impact.
- The affected version(s) (
mt5-quant --versionor theversionfield inCargo.toml). - Steps to reproduce, or a proof-of-concept input (e.g. a crafted MT5 report,
.setfile, or tool argument). - Any relevant logs, crashes, or sanitizer/fuzzer output.
- Acknowledgement: within 7 days of a complete report.
- Triage & severity assessment: within 14 days.
- Fix or mitigation plan: timeline depends on severity; critical issues are addressed as soon as a patch is ready, and a coordinated disclosure date is agreed with the reporter when a private advisory is used.
- Disclosure: once a fixed release is published, the advisory (if used) is made public and the fix is credited to the reporter unless they request anonymity.
There is no formal contractual SLA; reports are handled on a best-effort, case-by-case basis through the public channels above.
mt5-quant follows these practices to keep the code trustworthy:
- Input validation at every tool boundary. All 89 MCP tool handlers in
src/tools/handlers/parse and validate their arguments at the entry point (using helpers such asrequired_str,resolve_report, andprepare_analysis). Untrusted data — tool arguments, the filesystem, and MT5 output — is never trusted inside core logic. - No hardcoded secrets. The source tree contains no embedded credentials,
API tokens, or private keys. The only secrets are user-provided: a local
config/mt5-quant.yaml(gitignored) holding the Wine/MT5 paths, and an optional release PAT used only in CI. Neither is persisted by the project itself. - Safe parsing of untrusted MT5 reports. Backtest/optimization reports come
from MetaTrader 5 as either HTML or SpreadsheetML XML (
.htm.xml, Build 48+). Parsers insrc/analytics/extract.rsandsrc/optimization/parser.rsuse explicit, bounded parsing (noeval, no regex-backed HTML/script execution) and fail fast on malformed input. These parsers are additionally exercised by the cargo-fuzz harness infuzz/(seedynamic_analysis*criteria). - Least privilege at runtime. The architecture enforces a single MT5 instance and never runs two backtests in parallel on the same Wine prefix. When no display is available the pipeline falls back to headless/Xvfb mode; the only external process launched is the user-configured Wine/MT5 binary.
- Dependency auditing in CI. Every push and pull request runs
cargo-deny(license + advisory + source checks) and the GitHubdependency-reviewaction. Vulnerable or non-compliant dependencies block the build via theprotect-masterruleset's required status checks. - Fuzzing of parsers.
fuzz/contains libFuzzer targets that feed arbitrary bytes into the report/metrics parsers with debug assertions and integer- overflow traps enabled, catching panics and invariant violations. - Isolation of the Wine/MT5 sandbox. MT5 runs under the user's Wine prefix, which is isolated from the host shell environment. The server does not grant MT5 any elevated privileges.
- Static analysis. Clippy runs with
-D warnings(warnings fail the build) and GitHub CodeQL (Rust) scans every push/PR.
The codebase is deliberately written to avoid well-known Rust/systems pitfalls:
- Integer overflow. Rust's release builds disable overflow checks by
default, so arithmetic on deal volumes, prices, and cumulative PnL uses
checked_*/saturating_*operations or is validated at parse time. The fuzz harness re-enablesdebug-assertionsso overflow traps fire during testing. - Path traversal in
.setfile handling..setfiles (UTF-16LE parameter files for EAs) are read/written through helpers insrc/tools/handlers/setfiles.rs. Paths are resolved against the configured tester profiles directory and normalized; user-supplied filenames are never interpolated into shell commands and are confined to that directory. - UTF-16LE encoding pitfalls. MetaTrader strips
||Yflags from UTF-8.setfiles, so all writes use UTF-16LE with an explicit BOM andchmod 444. The helpers never assume UTF-8, avoiding silent mojibake / flag loss. - Avoiding
unsafe. The crate uses nounsafeblocks in application logic; all FFI with Wine/MT5 is performed via thestd::processAPI and the systemcurl/gpgbinaries, not raw pointer manipulation. - Proper error propagation. Errors are returned as typed
Results and propagated with?; there are no swallowed errors or emptycatch-and-ignore paths. Invalid states halt fast with descriptive messages (Fail-Fast philosophy) rather than being silently patched. - No injection via shell. Wine/MT5 is launched with an argument vector (not a constructed shell string), eliminating command-injection from config values.
To satisfy delivery_unsigned, release artifacts are GPG-signed:
- Each GitHub Release publishes the macOS (
mcp-mt5-quant-macos-arm64.tar.gz) and Linux (mcp-mt5-quant-linux-x64.tar.gz) binaries plus their detached ASCII-armored signatures (*.tar.gz.asc), and the project's public key atdocs/signing-key.asc. - The signing key is an RSA-4096 key generated for the
mt5-quant releasesidentity. The public half is committed to the repository; the private half is held only in CI secrets (GPG_PRIVATE_KEY,GPG_PASSPHRASE) and is never committed. - Signing is performed in CI (
release.yml) withgpg --detach-sign --armorand is guarded so the release still succeeds if the signing secrets are not configured — it simply omits the signatures in that case.
# 1. Import the project public key
gpg --import docs/signing-key.asc
# 2. Verify a release artifact
gpg --verify mcp-mt5-quant-linux-x64.tar.gz.asc \
mcp-mt5-quant-linux-x64.tar.gz
# A good signature from "mt5-quant releases <releases@mt5-quant.local>"
# confirms the artifact was produced by the project's release pipeline.Security fixes are applied to the latest released version on the master
branch. Older releases are not back-ported; users should upgrade to the current
release to receive fixes.