{"record":{"id":"c673d0a82897a51d","repo":"hashicorp/packer","slug":"dots-are-not-allowed","errorCode":null,"errorMessage":"dots are not allowed","messagePattern":"dots are not allowed","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"hcl2template/addrs/plugin.go","lineNumber":75,"sourceCode":"// of an addrs.Plugin is already a hostname and it's ideal to use exactly\n// the same case folding and normalization rules for all of the parts.\n//\n// It's valid to pass the result of this function as the argument to a\n// subsequent call, in which case the result will be identical.\nfunc ParsePluginPart(given string) (string, error) {\n\tif len(given) == 0 {\n\t\treturn \"\", fmt.Errorf(\"must have at least one character\")\n\t}\n\n\t// We're going to process the given name using the same \"IDNA\" library we\n\t// use for the hostname portion, since it already implements the case\n\t// folding rules we want.\n\t//\n\t// The idna library doesn't expose individual label parsing directly, but\n\t// once we've verified it doesn't contain any dots we can just treat it\n\t// like a top-level domain for this library's purposes.\n\tif strings.ContainsRune(given, '.') {\n\t\treturn \"\", fmt.Errorf(\"dots are not allowed\")\n\t}\n\n\t// We don't allow names containing multiple consecutive dashes, just as\n\t// a matter of preference: they look confusing, or incorrect.\n\t// This also, as a side-effect, prevents the use of the \"punycode\"\n\t// indicator prefix \"xn--\" that would cause the IDNA library to interpret\n\t// the given name as punycode, because that would be weird and unexpected.\n\tif strings.Contains(given, \"--\") {\n\t\treturn \"\", fmt.Errorf(\"cannot use multiple consecutive dashes\")\n\t}\n\n\tresult, err := idna.Lookup.ToUnicode(given)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"must contain only letters, digits, and dashes, and may not use leading or trailing dashes: %w\", err)\n\t}\n\n\treturn result, nil\n}","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/hashicorp/packer/blob/eb36e3c3e48a036f3e8cc94087636ee72e1303c9/hcl2template/addrs/plugin.go#L57-L93","documentation":"Plugin address parts must be single labels — dots would blur the boundary between hostname, namespace, and name segments. ParsePluginPart rejects any part containing '.', and as a side effect prevents punycode 'xn--' labels being smuggled in via dotted names.","triggerScenarios":"Passing a part containing a dot, e.g. ParsePluginPart(\"my.plugin\") or a required_plugins source like github.com/hashicorp/some.name where the name itself contains a dot.","commonSituations":"Users putting versions or file extensions in plugin names (amazon-1.0), or confusing the plugin address scheme with Go import paths.","solutions":["Remove dots from the plugin name/namespace part; use dashes instead","Keep the structure hostname/namespace/name with no extra dots inside each part","Use version constraints syntax (version = \">= 1.0.0\") instead of encoding versions in the name"],"exampleFix":"// before\nsource = \"github.com/hashicorp/my.plugin\"\n// after\nsource = \"github.com/hashicorp/my-plugin\"","handlingStrategy":"validation","validationCode":"func hasNoDots(addr string) bool {\n  for _, p := range strings.Split(addr, \"/\") { if strings.Contains(p, \".\") && strings.Count(addr, \"/\") > 0 && isNameOrNs(p) { return false } }\n  return true\n}","typeGuard":"func validPluginPart(s string) bool { return len(s) > 0 && !strings.ContainsRune(s, '.') }","tryCatchPattern":null,"preventionTips":["Use dashes, never dots, in plugin names and namespaces","Encode version constraints in the `version` attribute, not the name","Treat plugin addresses as host/namespace/name labels, not file paths"],"tags":["hcl2","plugin-address","validation"],"backgroundTag":"invalid-identifier-format","analyzedSha":"eb36e3c3e48a036f3e8cc94087636ee72e1303c9","analyzedAt":"2026-09-05T13:20:43.127Z","contentChangedAt":"2026-09-05T13:20:43.127Z","schemaVersion":2},"datasetVersion":"2026-09-12T17:17:11.597Z"}