//go:build !windows package builder import ( "fmt" "log" "os/exec" "strings" ) func signingToolMissingNote() string { return "Code signing requested but osslsigncode is not installed — apt install osslsigncode (or brew install osslsigncode)." } func (h *Handler) signingToolAvailable() bool { if tool := strings.TrimSpace(h.policy.Sign.ToolPath); tool != "" { if _, err := exec.LookPath(tool); err == nil { return true } } _, err := exec.LookPath("osslsigncode") return err == nil } // shouldSignBuild returns true when signing is configured AND osslsigncode is available. // On Linux/macOS we can sign Windows PE files with osslsigncode + a PFX certificate. // Install: apt install osslsigncode / brew install osslsigncode func (h *Handler) shouldSignBuild(req *BuildRequest) bool { if !h.policy.Sign.Enabled || strings.TrimSpace(h.policy.Sign.CertThumbprint) == "" { return false } if !req.SignBuild { return false } _, err := exec.LookPath("osslsigncode") if err != nil { log.Printf("[Forge] sign requested but osslsigncode not found — install with: apt install osslsigncode (or brew install osslsigncode)") return false } return true } // signExecutable signs a Windows PE binary using osslsigncode. // Requires: osslsigncode on PATH and a PFX certificate file set in sign_tool_path. // The sign_cert_thumbprint field is repurposed as the path to the .pfx file on non-Windows. func (h *Handler) signExecutable(path string) error { policy := h.policy.Sign pfxPath := strings.TrimSpace(policy.CertThumbprint) if pfxPath == "" { return fmt.Errorf("sign_cert_thumbprint must contain the path to a .pfx certificate file on Linux/macOS") } tsURL := strings.TrimSpace(policy.TimestampURL) if tsURL == "" { tsURL = "http://timestamp.digicert.com" } // osslsigncode usage: osslsigncode sign -pkcs12 -ts -in -out // We sign in-place by writing to a temp path then replacing. tmpPath := path + ".signed" args := []string{ "sign", "-pkcs12", pfxPath, "-ts", tsURL, "-h", "sha2", "-in", path, "-out", tmpPath, } osslBin, _ := exec.LookPath("osslsigncode") cmd := exec.Command(osslBin, args...) out, err := cmd.CombinedOutput() if err != nil { return fmt.Errorf("osslsigncode: %w (%s)", err, strings.TrimSpace(string(out))) } // Replace the original with the signed copy if err := exec.Command("mv", tmpPath, path).Run(); err != nil { return fmt.Errorf("could not replace binary with signed version: %w", err) } log.Printf("[Forge] Signed %s via osslsigncode", path) return nil }