{"record":{"id":"c14e7c25c2b9ee85","repo":"golang/go","slug":"invalid-module-version-q","errorCode":null,"errorMessage":"invalid module version %q","messagePattern":"invalid module version %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/cmd/go/internal/modload/list.go","lineNumber":342,"sourceCode":"\t}\n\n\treturn &modinfo.ModuleError{Err: err.Error()}\n}\n\n// ParsePathVersion parses arg expecting arg to be path@version. If there is no\n// '@' in arg, found is false, vers is \"\", and path is arg. This mirrors the\n// typical usage of strings.Cut. ParsePathVersion is meant to be a general\n// replacement for strings.Cut in module version parsing. If the version is\n// invalid, an error is returned. The version is considered invalid if it is\n// prefixed with '-' or '/', which can cause security problems when constructing\n// commands to execute that use the version.\nfunc ParsePathVersion(arg string) (path, vers string, found bool, err error) {\n\tpath, vers, found = strings.Cut(arg, \"@\")\n\tif !found {\n\t\treturn arg, \"\", false, nil\n\t}\n\tif len(vers) > 0 && (vers[0] == '-' || vers[0] == '/') {\n\t\treturn \"\", \"\", false, fmt.Errorf(\"invalid module version %q\", vers)\n\t}\n\treturn path, vers, true, nil\n}\n","sourceCodeStart":324,"sourceCodeEnd":346,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/cmd/go/internal/modload/list.go#L324-L346","documentation":"Thrown by ParsePathVersion (list.go:342) when a module path@version argument has a version string (the part after '@') that begins with '-' or '/'. These prefixes are rejected because they can cause security problems when the version is interpolated into shell commands or filesystem paths (e.g., a version like '-rf' could be interpreted as a command flag). This is a security boundary, not merely a format check.","triggerScenarios":"Calling 'go get example.com/module@-foo' or 'go get example.com/module@/etc/passwd'. Any code path that calls ParsePathVersion with an arg containing '@' followed by a version starting with '-' or '/'. The function uses strings.Cut to split on '@', then inspects vers[0].","commonSituations":"User-supplied or script-generated module version strings that are not sanitized before being passed to go commands. Attempting to pass flags after '@' by mistake. Adversarial input in tooling that constructs module@version arguments.","solutions":["Sanitize or validate any user-supplied version string before appending it after '@' — reject versions starting with '-' or '/'.","Use canonical semver version strings (e.g. 'v1.2.3') that never start with these characters.","If building tooling on top of ParsePathVersion, wrap it and surface a clear validation error to the end user."],"exampleFix":"// before\nversion := getUserInput() // e.g. \"-rf\"\narg := modulePath + \"@\" + version\npath, vers, _, err := modload.ParsePathVersion(arg)\n\n// after — validate first\nif version != \"\" && (version[0] == '-' || version[0] == '/') {\n    return fmt.Errorf(\"invalid version: %q\", version)\n}\narg := modulePath + \"@\" + version","handlingStrategy":"validation","validationCode":"// Sanitize a user-supplied version before constructing path@version.\nfunc sanitizeVersion(v string) error {\n    if v != \"\" && (v[0] == '-' || v[0] == '/') {\n        return fmt.Errorf(\"invalid module version %q: must not start with '-' or '/'\", v)\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never interpolate raw user input into a path@version string without sanitization.","Prefer canonical semver versions (v1.2.3) which never start with '-' or '/'.","When building CLI tools that accept version arguments, validate before passing to go commands."],"tags":["security","module-version","validation","injection","go-get"],"backgroundTag":null,"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}