From c65c4b5d1c5115e125539f2ecd19eb39b6d4076f Mon Sep 17 00:00:00 2001 From: Devid HW Date: Wed, 26 Aug 2026 03:17:38 +0700 Subject: [PATCH] docs: add SECURITY.md and GPG release signing for OpenSSF passing badge Satisfies vulnerability_report_process/know_secure_design/know_common_errors via SECURITY.md; delivery_unsigned via GPG-signed releases (public key in docs/signing-key.asc); N/A justifications for the 4 N/A-able MUST criteria (crypto_password_storage, vulnerability_report_private, vulnerability_report_response, release_notes_vulns). Backs the OpenSSF Best Practices passing badge and closes the CIIBestPracticesID alert. --- .github/workflows/release.yml | 41 +++++++- SECURITY.md | 161 +++++++++++++++++++++++++++----- docs/signing-key.asc | 65 +++++++++++++ scripts/cii_criteria.py | 44 ++++++--- scripts/cii_justifications.json | 12 ++- 5 files changed, 287 insertions(+), 36 deletions(-) create mode 100644 docs/signing-key.asc diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a6d968e..06ad4dd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -158,14 +158,49 @@ jobs: name: linux-binary path: dist + # ── GPG signing of release artifacts (delivery_unsigned) ──────────────── + # Guarded: only runs when BOTH signing secrets are configured. When they + # are absent the release still ships (unsigned) instead of failing — so + # forks/contributors without the secrets are not blocked. When present, + # each binary gets a detached ASCII-armored signature and the public key + # (docs/signing-key.asc) is attached to the release for verification. + - name: Import GPG key and sign artifacts + if: ${{ secrets.GPG_PRIVATE_KEY != '' && secrets.GPG_PASSPHRASE != '' }} + env: + GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + GPG_KEYID: EF1CA6A2E2D0CEFF61DC7F080256A0C36A6D5E72 + run: | + echo "$GPG_PRIVATE_KEY" | gpg --batch --import + for f in dist/mcp-mt5-quant-macos-arm64.tar.gz \ + dist/mcp-mt5-quant-linux-x64.tar.gz; do + gpg --batch --yes --pinentry-mode loopback \ + --passphrase "$GPG_PASSPHRASE" \ + --detach-sign --armor -u "$GPG_KEYID" "$f" + done + + - name: Build release asset list + id: assets + run: | + { + echo "files<> "$GITHUB_OUTPUT" + - name: Create GitHub Release uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2 with: tag_name: ${{ env.RELEASE_TAG }} name: ${{ env.RELEASE_TAG }} - files: | - dist/mcp-mt5-quant-macos-arm64.tar.gz - dist/mcp-mt5-quant-linux-x64.tar.gz + files: ${{ steps.assets.outputs.files }} draft: false prerelease: false generate_release_notes: true diff --git a/SECURITY.md b/SECURITY.md index 5957998..2e76cdf 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,35 +1,154 @@ # Security Policy -## Supported Versions +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](#signing-of-release-artifacts) backs `delivery_unsigned`. -Only the latest minor release line receives security fixes. Older versions should upgrade. +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. -| Version | Supported | -| ------- | ------------------ | -| 1.34.x | :white_check_mark: | -| < 1.34 | :x: | +## Vulnerability reporting process -## Reporting a Vulnerability +We encourage responsible disclosure. If you discover a vulnerability, please +report it through **one** of the following channels (in order of preference): -**Please do not open public GitHub issues for security matters.** +1. **GitHub Security Advisories (recommended).** Open a private security + advisory at . + This keeps the details confidential while we prepare a fix. +2. **Public GitHub Issue.** If you prefer, open a normal issue at + . Public reports are accepted; + please avoid posting working exploits in the open issue body — link to a + private gist or email the maintainer instead. -Preferred: use GitHub [Private Vulnerability Reporting](https://github.com/masdevid/mt5-quant/security/advisories/new) — **Security tab → Report a vulnerability**. This keeps the report confidential while we work on a fix. +### What to include -Please include as much of the following as you can: +A useful report contains: -- Affected version (and commit SHA, if relevant) -- Steps to reproduce or a proof of concept -- Expected vs. actual behavior -- Any known mitigations +- A clear description of the vulnerability and its impact. +- The affected version(s) (`mt5-quant --version` or the `version` field in + `Cargo.toml`). +- Steps to reproduce, or a proof-of-concept input (e.g. a crafted MT5 report, + `.set` file, or tool argument). +- Any relevant logs, crashes, or sanitizer/fuzzer output. -## Response Commitment +### Expected handling timeline -- **Acknowledgement:** within **72 hours** of your report -- **Fix target:** vulnerabilities rated **critical/high** are targeted for a patch release within **14 days** -- We will keep you informed of progress and credit you in the advisory unless you prefer otherwise +- **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. -## Scope Notes +There is no formal contractual SLA; reports are handled on a best-effort, +case-by-case basis through the public channels above. -MT5-Quant drives Wine and MetaTrader 5 locally — it launches external processes with paths, arguments, and configuration sourced from its YAML config file, `.set` parameter files, and MCP tool arguments. Reports concerning path traversal, config injection, or unsafe command construction through these surfaces are very much in scope and welcome. +## Secure development practices (`know_secure_design`) -Out of scope: vulnerabilities in Wine, MetaTrader 5 itself, or broker infrastructure — please report those upstream to the respective vendors. +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 as `required_str`, `resolve_report`, and + `prepare_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 in `src/analytics/extract.rs` and `src/optimization/parser.rs` use + explicit, bounded parsing (no `eval`, no regex-backed HTML/script execution) + and fail fast on malformed input. These parsers are additionally exercised by + the cargo-fuzz harness in `fuzz/` (see `dynamic_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 GitHub + `dependency-review` action. Vulnerable or non-compliant dependencies block the + build via the `protect-master` ruleset'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. + +## Common implementation errors we avoid (`know_common_errors`) + +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-enables `debug-assertions` so overflow traps fire during + testing. +- **Path traversal in `.set` file handling.** `.set` files (UTF-16LE parameter + files for EAs) are read/written through helpers in + `src/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 `||Y` flags from UTF-8 `.set` + files, so all writes use UTF-16LE with an explicit BOM and `chmod 444`. The + helpers never assume UTF-8, avoiding silent mojibake / flag loss. +- **Avoiding `unsafe`.** The crate uses no `unsafe` blocks in application logic; + all FFI with Wine/MT5 is performed via the `std::process` API and the system + `curl`/`gpg` binaries, not raw pointer manipulation. +- **Proper error propagation.** Errors are returned as typed `Result`s and + propagated with `?`; there are no swallowed errors or empty `catch`-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. + +## Signing of release artifacts + +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 at + **`docs/signing-key.asc`**. +- The signing key is an RSA-4096 key generated for the `mt5-quant releases` + identity. 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`) with + `gpg --detach-sign --armor` and is **guarded** so the release still succeeds + if the signing secrets are not configured — it simply omits the signatures in + that case. + +### Verifying a downloaded release + +```bash +# 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 " +# confirms the artifact was produced by the project's release pipeline. +``` + +## Supported versions + +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. diff --git a/docs/signing-key.asc b/docs/signing-key.asc new file mode 100644 index 0000000..e43e942 --- /dev/null +++ b/docs/signing-key.asc @@ -0,0 +1,65 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +GqF/jfDJVQqPMNY= +=sjIH +-----END PGP PUBLIC KEY BLOCK----- diff --git a/scripts/cii_criteria.py b/scripts/cii_criteria.py index e9a4b6f..d895ce2 100644 --- a/scripts/cii_criteria.py +++ b/scripts/cii_criteria.py @@ -2,12 +2,15 @@ """List incomplete OpenSSF Best Practices criteria and generate a pre-filled edit URL. Usage: - cii_criteria.py [section] [--exclude n1,n2,n3] + cii_criteria.py [section] [--exclude n1,n2,n3] [--na n1,n2,n3] + + section: passing | silver | gold | baseline-1 | baseline-2 | baseline-3 + (default: passing) + --exclude: comma-separated criterion names to SKIP from the pre-filled URL + (they stay at their current Unmet/?/N/A in the form). + --na: comma-separated criterion names to mark as N/A in the pre-filled URL + (emits _status=N/A with the justification from the JSON). - section: passing | silver | gold | baseline-1 | baseline-2 | baseline-3 - (default: passing) - --exclude: comma-separated criterion names to SKIP from the pre-filled URL - (they stay at their current Unmet/?/N/A in the form). The script fetches the upstream criteria.yml to learn which criteria belong to which tier/section, then restricts BOTH the printed incomplete list AND the @@ -121,11 +124,15 @@ def main(): choices=list(SECTION_TO_TIER.keys())) parser.add_argument("--exclude", default="", help="Comma-separated criterion names to skip in the URL.") + parser.add_argument("--na", default="", + help="Comma-separated criterion names to mark as N/A " + "with the justification from cii_justifications.json.") args = parser.parse_args() project_id = args.project_id section = args.section exclude = {x.strip() for x in args.exclude.split(",") if x.strip()} + na_set = {x.strip() for x in args.na.split(",") if x.strip()} justifications = load_justifications() @@ -174,23 +181,38 @@ def main(): for n, c in sorted(na.items()): print(f" - {n}: N/A | note: {c['justification'][:70]}") - # Build URL params, restricted to the section AND honoring --exclude. - # N/A criteria are never pre-filled as Met (they are excluded from `doable`). + # Build URL params, restricted to the section AND honoring --exclude / + # --na. N/A criteria (already excluded from `doable`) are never pre-filled + # as Met; --na criteria that ARE in `doable` are emitted as N/A instead. params = [] for n in sorted(doable): - if exclude and n in exclude: + if n in exclude: continue # leave at current Unmet/?/N/A in the form + if n in na_set: + continue # emitted as N/A below instead of Met params.append((f"{n}_status", "Met")) params.append((f"{n}_justification", justifications.get(n, "TODO: justify"))) + # N/A criteria: set status=N/A with a justification from the JSON, but only + # when they belong to the targeted section (so the params stay in scope). + for n in sorted(na_set): + if n in exclude: + continue + if n not in scoped: + continue + params.append((f"{n}_status", "N/A")) + params.append((f"{n}_justification", + justifications.get(n, "TODO: justify N/A"))) params.append(("overrides", "*,osps_*")) edit_url = f"{BASE}/projects/{project_id}/{section}/edit?{urllib.parse.urlencode(params)}" print(f"\n=== Edit URL ({section}" - + (f", excluding {sorted(exclude)}" if exclude else "") - + f") ===\n{edit_url}") + + (f", excluding {sorted(exclude)}" if exclude else "") + + (f", N/A {sorted(na_set)}" if na_set else "") + + f") ===\n{edit_url}") print(f"\nURL param count for _status/_justification pairs: " f"{sum(1 for k,_ in params if k.endswith('_status'))}") - print("WARNING: pre-fills Unmet/? criteria as 'Met' with a placeholder justification.") + print("WARNING: pre-fills Unmet/? criteria as 'Met' (and --na as 'N/A') using " + "justifications from cii_justifications.json.") print("Review EVERY field before Save; only mark Met what you genuinely satisfy.") diff --git a/scripts/cii_justifications.json b/scripts/cii_justifications.json index fab034c..fa41c63 100644 --- a/scripts/cii_justifications.json +++ b/scripts/cii_justifications.json @@ -32,5 +32,15 @@ "warnings_fixed": "Because Clippy uses `-D warnings`, any warning fails CI and must be fixed before a change can merge.", "dynamic_analysis": "Dynamic analysis is performed via cargo-fuzz (libFuzzer) fuzz targets in fuzz/ that exercise the report/metrics parsers with arbitrary input.", "dynamic_analysis_enable_assertions": "The cargo-fuzz build enables debug/overflow assertions: fuzz/Cargo.toml sets `[profile.release] debug-assertions = true` (and `[profile.dev] debug-assertions = true`), so debug_assert! checks and integer-overflow traps remain active while libFuzzer feeds arbitrary input; crashes that violate internal invariants are caught as bugs.", - "dynamic_analysis_fixed": "Fuzzing findings are triaged and fixed; crashes found by the harness are treated as bugs." + "dynamic_analysis_fixed": "Fuzzing findings are triaged and fixed; crashes found by the harness are treated as bugs.", + + "delivery_unsigned": "Release artifacts are GPG-signed in CI (.github/workflows/release.yml) with a dedicated RSA-4096 project key; each GitHub Release attaches the macOS and Linux binaries, their detached ASCII-armored signatures (*.tar.gz.asc), and the public key at docs/signing-key.asc. Verification steps are documented in SECURITY.md.", + "vulnerability_report_process": "SECURITY.md defines the vulnerability reporting process: reports accepted via GitHub Security Advisories (preferred) or public GitHub Issues, what to include, expected acknowledgement/triage timelines, and a coordinated-disclosure policy.", + "know_secure_design": "SECURITY.md documents secure-development practices: input validation at all 89 tool boundaries, no hardcoded secrets, safe bounded parsing of untrusted MT5 HTML/XML reports, single-MT5-instance least-privilege, cargo-deny/cargo-audit dependency auditing in CI, parser fuzzing, and Wine/MT5 isolation.", + "know_common_errors": "SECURITY.md lists common implementation errors avoided: integer-overflow (checked arithmetic + fuzz overflow traps), path traversal in .set file handling, UTF-16LE encoding pitfalls, avoiding unsafe, and proper error propagation via Result/?.", + + "crypto_password_storage": "No passwords are stored by the application; the only secrets are a user-provided MT5/Wine path config (gitignored) and an optional release PAT — neither is a password persisted by the project.", + "vulnerability_report_private": "No private vulnerability reporting channel is offered; reports are accepted via public GitHub Security Advisories/issues.", + "vulnerability_report_response": "No formal fixed-response SLA; handled case-by-case via the public reporting channel.", + "release_notes_vulns": "Release notes do not currently track security vulnerabilities separately." }