{"record":{"id":"ebb0969c3210a770","repo":"gitbutlerapp/gitbutler","slug":"file-suffix-is-not-allowed-must-be-one-of","errorCode":null,"errorMessage":"File suffix '{}' is not allowed. Must be one of: {}","messagePattern":"File suffix '(.+?)' is not allowed\\. Must be one of: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but/src/tui/get_text.rs","lineNumber":66,"sourceCode":"}\n\n/// Launches the user's preferred text editor to edit some `initial_text`,\n/// identified by a `filename_safe_intent` to help the user understand what's wanted of them.\n/// Note that this string must be valid in filenames.\n///\n/// If the user has an external editor configured (via `GIT_EDITOR`, `core.editor`, or `EDITOR`),\n/// that editor is used. Otherwise, the built-in TUI editor is launched.\n///\n/// Returns the edited text (*without known encoding*) verbatim.\npub fn from_editor(\n    filename_safe_intent: &str,\n    initial_text: &str,\n    rest_text: Option<&str>,\n    file_suffix: &str,\n) -> Result<BString> {\n    const ALLOWED_SUFFIXES: &[&str] = &[\".txt\", \".md\", \".patch\"]; // feel free to add more allowed suffixes\n    if !ALLOWED_SUFFIXES.contains(&file_suffix) {\n        bail!(\n            \"File suffix '{}' is not allowed. Must be one of: {}\",\n            file_suffix,\n            ALLOWED_SUFFIXES.join(\", \")\n        );\n    }\n\n    match get_editor_command() {\n        Some(editor_cmd) => from_external_editor(\n            &editor_cmd,\n            filename_safe_intent,\n            initial_text,\n            rest_text,\n            file_suffix,\n        ),\n        None => from_builtin_editor(filename_safe_intent, initial_text, rest_text),\n    }\n}\n","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but/src/tui/get_text.rs#L48-L84","documentation":"from_editor writes text to a temp file and launches an external or built-in editor; the file suffix is restricted to an explicit allowlist (.txt, .md, .patch) to keep editor behavior predictable. Any other suffix is a programming error by the internal caller and is rejected before any editor or temp file is touched.","triggerScenarios":"An internal call site passes a suffix outside ALLOWED_SUFFIXES - e.g. \".log\", \"txt\" without a leading dot, or a user-supplied extension forwarded unchecked into file_suffix.","commonSituations":"Adding new TUI flows that edit files, refactors that change the suffix constant, forwarding untrusted filenames into the editor helper.","solutions":["Use one of the allowed suffixes: .txt, .md, or .patch","If a new suffix is genuinely needed, add it to ALLOWED_SUFFIXES in crates/but/src/tui/get_text.rs (the comment invites additions)","Sanitize user-supplied extensions by mapping them onto .txt"],"exampleFix":"// before\nfrom_editor(\"commit-msg\", &text, None, \".log\")?;\n// after\nfrom_editor(\"commit-msg\", &text, None, \".txt\")?;","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"const ALLOWED_SUFFIXES: &[&str] = &[\".txt\", \".md\", \".patch\"];\nfn is_allowed_suffix(suffix: &str) -> bool {\n    ALLOWED_SUFFIXES.contains(&suffix)\n}\n// guard every call site:\nassert!(is_allowed_suffix(file_suffix), \"unsupported suffix {file_suffix}\");","tryCatchPattern":null,"preventionTips":["Keep the allowlist in one place and reference it from all callers","Never derive file_suffix from untrusted filenames - map onto .txt","Add a unit test covering each allowed suffix plus one rejected value"],"tags":["tui","editor","validation","file-extension","internal"],"backgroundTag":"file-extension-not-allowed","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}