{"record":{"id":"e65769571dd7f112","repo":"t8y2/dbx","slug":"dbx-public-base-path-contains-invalid-characters","errorCode":null,"errorMessage":"DBX_PUBLIC_BASE_PATH contains invalid characters","messagePattern":"DBX_PUBLIC_BASE_PATH contains invalid characters","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/dbx-web/src/main.rs","lineNumber":99,"sourceCode":"fn web_agent_dir(data_dir: &std::path::Path) -> std::path::PathBuf {\n    web_agent_dir_from_env(data_dir, std::env::var(\"DBX_AGENT_DIR\").ok())\n}\n\nfn web_agent_dir_from_env(data_dir: &std::path::Path, agent_dir: Option<String>) -> std::path::PathBuf {\n    agent_dir.map(std::path::PathBuf::from).unwrap_or_else(|| data_dir.join(\"agents\"))\n}\n\nfn normalize_public_base_path(value: Option<String>) -> String {\n    let trimmed = value\n        .unwrap_or_else(|| \"/\".to_string())\n        .split(['?', '#'])\n        .next()\n        .unwrap_or(\"/\")\n        .trim()\n        .trim_matches('/')\n        .to_string();\n    if trimmed.chars().any(|ch| ch.is_ascii_control() || ch.is_ascii_whitespace() || matches!(ch, ';' | ',')) {\n        panic!(\"DBX_PUBLIC_BASE_PATH contains invalid characters\");\n    }\n    if trimmed.is_empty() {\n        \"/\".to_string()\n    } else {\n        format!(\"/{trimmed}\")\n    }\n}\n\nfn add_public_base_path_redirect<S>(app: Router<S>, public_base_path: &str) -> Router<S>\nwhere\n    S: Clone + Send + Sync + 'static,\n{\n    if public_base_path == \"/\" {\n        return app;\n    }\n\n    // Derive the target from the configured base path so single- and multi-segment prefixes both work.\n    let redirect_target = format!(\"{public_base_path}/\");","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/t8y2/dbx/blob/c0390bff16418b651f4728520d99adf8ce48829a/crates/dbx-web/src/main.rs#L81-L117","documentation":"normalize_public_base_path sanitizes the DBX_PUBLIC_BASE_PATH environment variable into a leading-slash URL path prefix. It panics when the value contains ASCII control characters, whitespace, or the characters ';' or ',', which would corrupt routing/URL composition downstream. The crate refuses to start with an unsafe base path rather than silently normalizing it.","triggerScenarios":"Setting DBX_PUBLIC_BASE_PATH to a value containing spaces, tabs, newlines, control characters, semicolons, or commas (e.g. DBX_PUBLIC_BASE_PATH=\"/api, v2\" or a path copied with a trailing newline from a shell/CI variable).","commonSituations":"Copy-pasting a path with hidden whitespace from docs or Slack, multi-value env vars built with commas/semicolons by mistake, or CI secret managers injecting values with trailing newlines.","solutions":["Remove spaces, control characters, semicolons, and commas from DBX_PUBLIC_BASE_PATH (e.g. use /api/v2 not /api, v2)","Print the value with delimiters (printf '%q' \"$DBX_PUBLIC_BASE_PATH\") to reveal hidden whitespace/newlines before starting the app","If the variable is unset/empty, the code already defaults to \"/\" — just unset it instead of passing a malformed value"],"exampleFix":"// before\nDBX_PUBLIC_BASE_PATH=\"/api, v2\"\n// after\nDBX_PUBLIC_BASE_PATH=\"/api/v2\"","handlingStrategy":"validation","validationCode":"fn valid_base_path(v: &str) -> bool {\n    !v.chars().any(|c| c.is_ascii_control() || c.is_ascii_whitespace() || matches!(c, ';' | ','))\n}\nif let Ok(p) = std::env::var(\"DBX_PUBLIC_BASE_PATH\") {\n    assert!(valid_base_path(&p), \"DBX_PUBLIC_BASE_PATH has invalid characters\");\n}","typeGuard":"fn is_clean_path(v: &str) -> bool {\n    v.chars().all(|c| !c.is_ascii_control() && !c.is_ascii_whitespace() && !matches!(c, ';' | ','))\n}","tryCatchPattern":null,"preventionTips":["Quote env values in shell scripts to avoid accidental whitespace","Lint CI/secret-manager outputs for trailing newlines","Keep base paths to [A-Za-z0-9/_-] only"],"tags":["rust","configuration","env-var","panic","startup"],"backgroundTag":"invalid-env-var-value","analyzedSha":"c0390bff16418b651f4728520d99adf8ce48829a","analyzedAt":"2026-09-05T23:05:10.900Z","contentChangedAt":"2026-09-05T23:05:10.900Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}