Skip to content

Security Policy

chrisholloway5 edited this page Sep 8, 2026 · 2 revisions

Security Policy

Source of truth: .github/SECURITY.md, kept in the repository and copied here.

Supported Versions

Version Supported
6.2.x
< 6.2

Reporting a Vulnerability

Report security vulnerabilities privately through GitHub Security Advisories:

https://github.com/Progressiverobot/hmailserver/security/advisories/new

Do not open a public issue, discussion or pull request for a security problem, and do not post it to the hMailServer forum. A public report starts the clock for everyone running the server, not just for us.

If you cannot use GitHub Security Advisories, open a normal issue that says only that you have a security report and asks for a private channel — no technical detail — and we will open an advisory and invite you to it.

Is this a security report, or an issue?

Use this before writing anything. The wrong channel is not a formality: a public issue about a real vulnerability starts the clock for everyone running the server, and a private advisory about a scanner banner wastes the one maintainer's week.

flowchart TD
    Q0["You found something"] --> Q1{"Does exploiting it need an<br/>account that is already a<br/>server administrator?"}
    Q1 -- yes --> OUT1["Out of scope.<br/>Administrators can run scripts and<br/>change configuration by design"]
    Q1 -- no --> Q2{"Is it 'this listener has no TLS'<br/>on a listener the operator<br/>deliberately exposed?"}
    Q2 -- yes --> OUT2["Out of scope.<br/>The server lets you do that;<br/>it warns you first"]
    Q2 -- no --> Q3{"Is it scanner output with no<br/>demonstrated impact - a version<br/>banner, a TLS grade?"}
    Q3 -- yes --> OUT3["Out of scope.<br/>Show the impact and it stops<br/>being scanner output"]
    Q3 -- no --> Q4{"Is it a third-party dependency<br/>with no path from hMailServer<br/>to the affected code?"}
    Q4 -- yes --> OUT4["Report it upstream.<br/>Tell us too if we ship a vulnerable<br/>version - see Third-Party Binaries"]
    Q4 -- no --> Q5{"Remote code execution, memory<br/>corruption, auth or authz bypass,<br/>TLS or crypto weakness,<br/>SQL injection, privilege escalation?"}
    Q5 -- yes --> ADV["Open a private advisory.<br/>Never a public issue,<br/>discussion, PR or forum post"]
    Q5 -- "not sure" --> ADV
    Q5 -- "no, it is a plain defect" --> ISS["Open a normal issue.<br/>Questions go to Discussions"]
    ADV --> NOGH{"Cannot use GitHub<br/>Security Advisories?"}
    NOGH -- yes --> BARE["Open a normal issue saying only<br/>that you have a security report and<br/>asking for a private channel.<br/>No technical detail"]
Loading

What to expect

Stage Target
Acknowledgement that the report was received 5 working days
Initial assessment: reproduced or more information requested 10 working days
Fix released for a confirmed vulnerability 90 days from the acknowledgement

This is a small project — a single maintainer, not a staffed security team. Those are the targets we hold ourselves to, not a contractual commitment. If a deadline is going to slip, you will be told before it slips rather than after.

What happens after you report

sequenceDiagram
    autonumber
    participant R as Reporter
    participant M as Maintainer
    participant P as Public

    R->>M: Private advisory
    Note over M: Target: 5 working days
    M-->>R: Acknowledgement
    Note over M: Target: 10 working days
    M-->>R: Reproduced, or more information requested
    Note over M,R: If you plan to disclose sooner than 90 days,<br/>say so in the report so it can be planned around
    M->>M: Fix, with a negative-control test<br/>that fails on the pre-fix binary
    M->>M: Full regression gate on the stamped binary
    M->>P: Release, signed tag, signed assets
    Note over M: Target: fix within 90 days<br/>of the acknowledgement
    M->>P: Advisory published - at the fix<br/>or at 90 days, whichever is first
    M->>P: CVE requested through GitHub
    Note over R,P: You keep credit unless you ask not to be named.<br/>Nothing to sign. No bounties.
Loading

If a deadline is going to slip, you will be told before it slips rather than after. This is one maintainer, not a staffed security team; the table above is what the project holds itself to, not a contract.

Coordinated disclosure

We follow coordinated disclosure on a 90 day timetable. The advisory is published when a fix ships, or at 90 days, whichever comes first. If you intend to disclose sooner than that, say so in the report so we can plan around it rather than discover it.

You keep credit for the finding unless you ask us not to name you. We will not ask you to sign anything, and we do not pay bounties.

Once a fix is available it ships as a new build and the advisory is published with a CVE requested through GitHub.

Scope

hMailServer is a network-facing mail server (SMTP/IMAP/POP3 plus optional REST API, metrics and web-services listeners). Reports of particular interest:

  • Remote code execution or memory corruption in protocol handlers
  • Authentication or authorization bypass
  • TLS/crypto weaknesses (DANE, MTA-STS, DKIM/ARC, certificate handling)
  • SQL injection in the persistence layer
  • Privilege escalation via the Windows service or COM API

Out of scope

These are known properties of the design rather than vulnerabilities, and a report about them will be closed with a pointer back here:

  • Findings that require an account that is already a server administrator. Administrators can run scripts and change the configuration by design.
  • Missing hardening on a listener the operator deliberately exposed without TLS. The server lets you do that; it warns you first.
  • Output from an automated scanner with no demonstrated impact, including version-banner findings and TLS-configuration grades.
  • Vulnerabilities in a third-party dependency with no path from hMailServer to the affected code. Report those upstream; tell us as well if we ship a vulnerable version, and see Third-Party Binaries for what we ship and where it came from.

What makes a report easy to act on

None of this is required — a vague report of a real bug still beats no report — but each line below removes a round trip.

Include Why it matters here
The exact version, from Server status or hMailServer.exe file properties 6.2.x moves quickly; six releases shipped in the first eight days of September 2026
Which listener, and whether it was TLS or plaintext The four mail listeners, the metrics listener, the ManageSieve listener and the two on HttpServer have different code paths
Whether the connection was authenticated, and as what The boundary between actor A1 and A4 in the Assurance Case is the single biggest factor in severity
A byte-exact reproducer where the input is a message or a command Line endings matter: this tree is CRLF on purpose, and a reproducer normalised to LF often stops reproducing
The relevant ERROR log entry, with its HM number The number identifies the reporting site exactly
What you expected instead A refusal that should have been a deferral, or the reverse, is a real defect class here — see the 451 versus 550 argument in Architecture

What the project already does about each class

So a reporter can see what the baseline is before deciding whether something is news. The full argument is the Assurance Case.

Class Standing controls
Memory corruption in a parser /WX, /guard:cf, ASan-backed libFuzzer harnesses for MIME, CrashOracle so a swallowed access violation fails a regression test, CodeQL security-extended on the C++
SQL injection Parameterised statements exclusively; DDL only through the macro expanders; the rule is in CONTRIBUTING.md and enforced at review
Authentication and authorisation SCRAM-SHA-256 with optional channel binding, PBKDF2 by default with Argon2id and scrypt available, an optional pepper, per-account lockout, app passwords, TOTP, and a build check that keeps every folder-access decision inside ACLManager
TLS and certificate handling verify_peer with verify_fail_if_no_peer_cert, DANE with in-process DNSSEC validation, MTA-STS, a TLS 1.2 floor on the REST listener, one shared SslContextInitializer so all listeners get the same policy
Resource exhaustion Bounded thread pools, absolute session ceilings rather than idle timeouts, absolute per-request and per-connection deadlines on HttpServer, tarpits, per-IP submission limits
Supply chain Signed annotated tags, keyless Sigstore signatures in Rekor, SPDX and CycloneDX SBOMs, a manifest of every committed binary verified on every build, and a reproducible binary hash

Supply chain

Every release ships an SBOM (SPDX and CycloneDX) covering both the .NET and the native dependencies. Third-party binaries committed to this repository are inventoried, checksummed and verified on every build — see Third-Party Binaries.

Two things on a release can be verified independently of GitHub:

  • The release tag (from v6.2.23-alpha2 on) is an annotated tag signed with the maintainer's SSH key. The allow list is in the repository, so a clone can check it without trusting anything else:

    git -c gpg.ssh.allowedSignersFile=.github/allowed_signers verify-tag v6.2.23-alpha2
    
  • Every asset carries a Sigstore bundle (<asset>.cosign.bundle), keyless, bound to this repository's workflow identity and recorded in the public Rekor log. With cosign installed:

    cosign verify-blob \
      --bundle hMailServer-x.y.z-x64.exe.cosign.bundle \
      --certificate-identity-regexp '^https://github\.com/Progressiverobot/hmailserver/' \
      --certificate-oidc-issuer https://token.actions.githubusercontent.com \
      hMailServer-x.y.z-x64.exe
    

Neither replaces Authenticode: Windows SmartScreen and the UAC prompt do not read either signature, and the installer is not Authenticode-signed today.

Clone this wiki locally