{"record":{"id":"b7610ad3326baa30","repo":"hashicorp/terraform","slug":"subdirectory-path-q-leads-outside-of-the-module-p","errorCode":null,"errorMessage":"subdirectory path %q leads outside of the module package","messagePattern":"subdirectory path %q leads outside of the module package","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/getmodules/moduleaddrs/source_parsing.go","lineNumber":173,"sourceCode":"\tif isModuleSourceLocal(raw) {\n\t\treturn addrs.ModuleSourceRegistry{}, fmt.Errorf(\"can't use local directory %q as a module registry address\", raw)\n\t}\n\n\tsrc, err := tfaddr.ParseModuleSource(raw)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn addrs.ModuleSourceRegistry{\n\t\tPackage: src.Package,\n\t\tSubdir:  src.Subdir,\n\t}, nil\n}\n\nfunc parseModuleSourceRemote(raw string) (addrs.ModuleSourceRemote, error) {\n\tvar subDir string\n\traw, subDir = SplitPackageSubdir(raw)\n\tif strings.HasPrefix(subDir, \"../\") {\n\t\treturn addrs.ModuleSourceRemote{}, fmt.Errorf(\"subdirectory path %q leads outside of the module package\", subDir)\n\t}\n\n\t// A remote source address is really just a go-getter address resulting\n\t// from go-getter's \"detect\" phase, which adds on the prefix specifying\n\t// which protocol it should use and possibly also adjusts the\n\t// protocol-specific part into different syntax.\n\t//\n\t// Note that for historical reasons this can potentially do network\n\t// requests in order to disambiguate certain address types, although\n\t// that's a legacy thing that is only for some specific, less-commonly-used\n\t// address types. Most just do local string manipulation. We should\n\t// aim to remove the network requests over time, if possible.\n\tnorm, moreSubDir, err := NormalizePackageAddress(raw)\n\tif err != nil {\n\t\t// We must pass through the returned error directly here because\n\t\t// the getmodules package has some special error types it uses\n\t\t// for certain cases where the UI layer might want to include a\n\t\t// more helpful error message.","sourceCodeStart":155,"sourceCodeEnd":191,"githubUrl":"https://github.com/hashicorp/terraform/blob/d32a084675427f5ac3f7d2868578ef8b2c1dc525/internal/getmodules/moduleaddrs/source_parsing.go#L155-L191","documentation":"Returned by parseModuleSourceRemote after SplitPackageSubdir extracts a subdir that begins with '../'. Such a subdir would resolve outside the downloaded module package, so it is rejected as a safety/validity guard. The %q is the offending subdir.","triggerScenarios":"A module source like 'github.com/org/repo//../../escape' or any '...//../...' whose subdir portion starts with '../' reaches the remote parser.","commonSituations":"User tries to point a module at a sibling directory via parent traversal in a remote source; copy-paste of a relative path that worked locally but is meaningless for a remote package; attempted path-traversal.","solutions":["Use a subdir that stays within the package, e.g. '//modules/vpc'.","If you need a sibling package, declare it as a separate module source rather than traversing out.","Re-examine the part after '//' and remove any leading '../'."],"exampleFix":"# before (escapes package)\nsource = \"github.com/org/repo//../other-module\"\n\n# after (within package)\nsource = \"github.com/org/repo//modules/vpc\"","handlingStrategy":"validation","validationCode":"// Reject subdirs that escape the package before parsing.\n// _, sub := SplitPackageSubdir(raw)\n// if strings.HasPrefix(sub, \"../\") {\n//     return fmt.Errorf(\"subdir %q escapes the module package\", sub)\n// }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep subdirs within the package root (no leading '../').","Use separate module declarations for sibling packages.","Lint '//subdir' components in CI."],"tags":["module-address","subdir","security","path-traversal"],"backgroundTag":null,"analyzedSha":"d32a084675427f5ac3f7d2868578ef8b2c1dc525","analyzedAt":"2026-08-11T18:43:52.779Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}