{"record":{"id":"9dacd659154ee1d3","repo":"denoland/deno","slug":"invalid-kind-name-must-match-a-za-z0-9","errorCode":null,"errorMessage":"invalid {kind} {name:?}: must match [A-Za-z0-9 ._-]+, but contains {c:?}","messagePattern":"invalid (.+?) (.+?): must match \\[A-Za-z0-9 \\._-\\]\\+, but contains (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli/tools/desktop.rs","lineNumber":2987,"sourceCode":"}\n\n/// Reject any name we'd interpolate into a generated launcher script\n/// (POSIX shell on macOS/Linux, `.bat` on Windows). Even the\n/// double-quoted positions take expansions: `$`, backticks, `\\` in\n/// bash; `%` and `^` in cmd.exe. The launcher kind context (`kind`)\n/// is included in the error to make the failure easy to act on.\nfn validate_launcher_name(name: &str, kind: &str) -> Result<(), AnyError> {\n  if name.is_empty() {\n    bail!(\"invalid {kind}: name is empty\");\n  }\n  // ASCII-only, alphanumerics + a small whitelist of harmless\n  // punctuation. Spaces are allowed because real macOS .app bundles\n  // commonly have spaces in their executable names.\n  let bad = name.chars().find(|c| {\n    !(c.is_ascii_alphanumeric() || matches!(c, ' ' | '.' | '_' | '-'))\n  });\n  if let Some(c) = bad {\n    bail!(\n      \"invalid {kind} {name:?}: must match [A-Za-z0-9 ._-]+, but contains {c:?}\",\n    );\n  }\n  Ok(())\n}\n\n/// The pieces of `dylib_path` we feed into the bundlers, with proper\n/// error messages instead of `unwrap` panics on degenerate inputs like\n/// `--output /` or `--output .`.\nstruct DylibParts<'a> {\n  parent: &'a Path,\n  file_name: &'a std::ffi::OsStr,\n  app_name: String,\n}\n\nfn dylib_parts(dylib_path: &Path) -> Result<DylibParts<'_>, AnyError> {\n  let parent = dylib_path.parent().ok_or_else(|| {\n    deno_core::anyhow::anyhow!(","sourceCodeStart":2969,"sourceCodeEnd":3005,"githubUrl":"https://github.com/denoland/deno/blob/f7822238cab635a3a19f99f493f675fa81a7f9d8/cli/tools/desktop.rs#L2969-L3005","documentation":"The second validate_launcher_name check: names are restricted to ASCII alphanumerics plus space, '.', '_', '-'. Anything else — even inside double quotes — is dangerous because '$', backticks, and '\\' expand in POSIX shell and '%' / '^' expand in cmd.exe, so the generated launcher script could execute injected content. The error names the first offending character.","triggerScenarios":"An app name containing quotes, parentheses, '%', '$', backticks, slashes, or any non-ASCII character (accents, emoji, CJK). Examples that fail: \"My App (beta)\", \"Café\", \"100% Cool\", \"A&B\".","commonSituations":"Product/marketing names pasted straight into config; localized names with diacritics; names borrowed from an existing brand that uses punctuation.","solutions":["Rewrite the name using only [A-Za-z0-9 ._-] — replace punctuation with '-', '_' or spaces.","Transliterate accented characters (Café -> Cafe) or drop them.","Use the plain name for the launcher/app bundle and keep the fancy spelling for in-app UI text."],"exampleFix":"// before\n\"desktop\": { \"name\": \"Café (démo)\" }\n\n// after\n\"desktop\": { \"name\": \"Cafe Demo\" }","handlingStrategy":"type-guard","validationCode":"const LAUNCHER_NAME_RE = /^[A-Za-z0-9 ._-]+$/;\nexport function checkAppName(name: string): void {\n  if (!LAUNCHER_NAME_RE.test(name)) {\n    const bad = [...name].find((c) => !LAUNCHER_NAME_RE.test(c));\n    throw new Error(`app name contains disallowed character: ${JSON.stringify(bad)}`);\n  }\n}","typeGuard":"function isSafeLauncherName(name: string): boolean {\n  return /^[A-Za-z0-9 ._-]+$/.test(name);\n}","tryCatchPattern":null,"preventionTips":["Validate the name with the same whitelist in your project generator so bad names never reach the CLI.","Avoid punctuation-rich marketing names for the launcher; keep them for UI strings.","Remember the charset is ASCII-only — accents and emoji are rejected."],"tags":["desktop","launcher","input-validation","shell-injection","config"],"backgroundTag":"invalid-app-name","analyzedSha":"f7822238cab635a3a19f99f493f675fa81a7f9d8","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}