{
  "ticket_id": "CVE-2026-7474",
  "code_root": "external/nomad",
  "source": {
    "type": "cve",
    "cve_id": "CVE-2026-7474",
    "ghsa_id": "GHSA-hx53-77qj-8663",
    "repo": "https://github.com/hashicorp/nomad"
  },
  "facts": {
    "issue_summary": "In Nomad's dynamic host volume Create workflow, the attacker-supplied PluginID is passed as filename to NewHostVolumePluginExternal in client/hostvolumemanager/host_volume_plugin.go (around line 229 in v2.0.0). The function joins pluginDir and filename with filepath.Join, then os.Stat's the result. A PluginID containing ../ traverses outside the plugin directory. The resulting path is stored in HostVolumePluginExternal.Executable and later executed via exec.CommandContext during Fingerprint/Create/Delete, yielding arbitrary code execution as the Nomad client user. Server-side mitigation is bypassed when the attacker provides an explicit NodeID in the volume create request, because placeHostVolume skips the plugin feasibility check in that case.",
    "vulnerability_type": "Path traversal leading to arbitrary command execution in dynamic host volume plugin loader",
    "suspected_cwe": ["CWE-22", "CWE-78"],
    "affected_versions": "prior to v2.0.1 (confirmed vulnerable in v2.0.0, v1.10.5, v1.11.2)",
    "fixed_versions": ["v2.0.1"],
    "reproduce_version": "v2.0.0",
    "verify_fixed_version": "v2.0.1",
    "repo_url": "https://github.com/hashicorp/nomad.git",
    "checkout_ref": "v2.0.0",
    "code_root": "external/nomad",
    "attacker_access": "Authenticated Nomad user with host-volume-create ACL capability in a namespace. The attacker submits a HostVolumeCreate request with a malicious PluginID and an explicit NodeID, which bypasses the server-side plugin feasibility check and causes the client to execute an arbitrary binary.",
    "primary_entry_point": "NewHostVolumePluginExternal in client/hostvolumemanager/host_volume_plugin.go, the filepath.Join(pluginDir, filename) and os.Stat(executable) calls around line 229 (v2.0.0). Reached from HostVolumeManager.getPlugin -> ClientHostVolume.Create RPC after a server HostVolume.Create forwards the request.",
    "build_hint": "git clone https://github.com/hashicorp/nomad external/nomad && cd external/nomad && git checkout v2.0.0 && go build -o bin/nomad ./cmd/nomad. Requires Go 1.26.3 (per .go-version). Use the same checkout_ref for reproduce_version and verify_fixed_version for v2.0.1.",
    "trigger_hint": "PluginID string containing path traversal sequences (e.g., ../../bin/ls or ../../../usr/bin/id). The payload is delivered through the PluginID field of a HostVolumeCreateRequest (or volume specification HCL/JSON) with an explicit NodeID to bypass server-side placement validation.",
    "end_to_end_requirement": "Demonstrate that on v2.0.0, NewHostVolumePluginExternal resolves a traversal-containing PluginID to an executable path outside the configured plugin directory (and could execute it). On v2.0.1, the same payload is rejected by os.OpenRoot containment on the client, or by the server-side feasibility check in placeHostVolume even when NodeID is explicit. A direct Go unit test that calls NewHostVolumePluginExternal with the traversal payload is acceptable as long as it exercises the real function and the actual os.Stat/os.OpenRoot behavior.",
    "tested_under": "Nomad v2.0.0 vs v2.0.1. Direct function-level reproduction via NewHostVolumePluginExternal with a PluginID of ../../bin/ls."
  }
}
