{"record":{"id":"90c35f8f5a7173c3","repo":"gitbutlerapp/gitbutler","slug":"signature-is-empty-refusing-to-verify-without-a","errorCode":null,"errorMessage":"Signature is empty - refusing to verify without a valid signature","messagePattern":"Signature is empty - refusing to verify without a valid signature","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-installer/src/install.rs","lineNumber":40,"sourceCode":"        .arg(\"--version\")\n        .stdout(Stdio::null())\n        .stderr(Stdio::null())\n        .status()\n        .map(|s| s.success())\n        .unwrap_or(false)\n}\n\n/// Verify the signature for a CLI installable\npub(crate) fn verify_signature(\n    installable: &Path,\n    signature_b64: &str,\n    temp_dir: &Path,\n) -> Result<()> {\n    crate::ui::info(\"Verifying download signature...\");\n\n    // Validate signature is not empty - this is a security requirement\n    if signature_b64.trim().is_empty() {\n        bail!(\"Signature is empty - refusing to verify without a valid signature\");\n    }\n\n    // GitButler's minisign public key\n    let pubkey_str = \"RWTrOEI+im1XYA9RBwyxnzFN/evFzJhU1lbQ70LVayWH3WRo7xQnRLD2\";\n\n    // Parse the public key\n    let public_key = minisign_verify::PublicKey::from_base64(pubkey_str)\n        .context(\"Failed to parse public key\")?;\n\n    // Decode signature from base64 and write to file\n    // The signature format from the API is base64-encoded minisign signature file content\n    use base64::{Engine, engine::general_purpose::STANDARD};\n    let signature_bytes = STANDARD\n        .decode(signature_b64)\n        .context(\"Failed to decode signature from base64\")?;\n\n    // Write signature to temp file for minisign to parse\n    let signature_file = temp_dir.join(\"signature.minisig\");","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-installer/src/install.rs#L22-L58","documentation":"verify_signature() (crates/but-installer/src/install.rs:31-41) refuses to continue when the signature string is empty or whitespace-only. On macOS the value comes from platforms[<platform>].signature in the release JSON; an empty signature means the release cannot be proven authentic, and the installer will not skip minisign verification against GitButler's hardcoded public key. This is a deliberate security stop, not a parsing bug.","triggerScenarios":"The release JSON at app.gitbutler.com contains an empty signature for the platform entry; a caller of verify_signature passes an empty string because a field was misparsed or dropped; a release was published with the signing step failed or skipped.","commonSituations":"Nightly published during a signing pipeline failure; pinning a version released before signatures were added to metadata; caching layers that strip the signature field.","solutions":["Retry or pick another version/channel - the defect is in the published release metadata and cannot be fixed locally","Verify the release JSON at app.gitbutler.com/releases to confirm the signature field is empty for your platform","Never bypass signature verification to work around this; report the broken release to GitButler (GitHub issues)"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Before installing, confirm the release ships a usable signature for your platform\nlet release: serde_json::Value = client.get(&releases_url).send()?.json()?;\nlet sig = release[\"platforms\"][&platform][\"signature\"].as_str().unwrap_or(\"\");\nif sig.trim().is_empty() {\n    anyhow::bail!(\"release has no signature for {platform}; refusing to install\");\n}","typeGuard":null,"tryCatchPattern":"if let Err(e) = but_installer::run_installation_with_version(request, false) {\n    if e.to_string().contains(\"Signature is empty\") {\n        // upstream release defect: switch version/channel, never skip verification\n        return but_installer::run_installation_with_version(\n            but_installer::VersionRequest::Release, false);\n    }\n    return Err(e);\n}","preventionTips":["Never work around empty signatures by bypassing verification - treat it as a broken release","Pre-validate release metadata (signature non-empty) before starting an install","Pin to versions known to publish signed artifacts"],"tags":["rust","security","signature","minisign","release-metadata","but-installer"],"backgroundTag":"missing-signature","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","schemaVersion":2},"datasetVersion":"2026-08-31T22:30:34.772Z"}