diff --git a/email/send.go b/email/send.go index 0521cc4..94e5e6c 100644 --- a/email/send.go +++ b/email/send.go @@ -166,16 +166,16 @@ func (m *Mailer) Send(to, subject, htmlBody, textBody, unsubToken, dashboardURL, textFooter.WriteString("\n\n---\n") if keepAliveURL != "" { - htmlFooter.WriteString(fmt.Sprintf(`keep this digest active`, keepAliveURL)) - textFooter.WriteString(fmt.Sprintf("keep this digest active: %s\n", keepAliveURL)) + fmt.Fprintf(&htmlFooter, `keep this digest active`, keepAliveURL) + fmt.Fprintf(&textFooter, "keep this digest active: %s\n", keepAliveURL) } if dashboardURL != "" { if keepAliveURL != "" { htmlFooter.WriteString(" • ") } - htmlFooter.WriteString(fmt.Sprintf(`profile`, dashboardURL)) - textFooter.WriteString(fmt.Sprintf("profile: %s\n", dashboardURL)) + fmt.Fprintf(&htmlFooter, `profile`, dashboardURL) + fmt.Fprintf(&textFooter, "profile: %s\n", dashboardURL) } if unsubToken != "" { @@ -183,8 +183,8 @@ func (m *Mailer) Send(to, subject, htmlBody, textBody, unsubToken, dashboardURL, if dashboardURL != "" || keepAliveURL != "" { htmlFooter.WriteString(" • ") } - htmlFooter.WriteString(fmt.Sprintf(`unsubscribe`, unsubURL)) - textFooter.WriteString(fmt.Sprintf("unsubscribe: %s\n", unsubURL)) + fmt.Fprintf(&htmlFooter, `unsubscribe`, unsubURL) + fmt.Fprintf(&textFooter, "unsubscribe: %s\n", unsubURL) } htmlFooter.WriteString("

") @@ -219,25 +219,25 @@ func (m *Mailer) Send(to, subject, htmlBody, textBody, unsubToken, dashboardURL, var msg strings.Builder for k, v := range headers { - msg.WriteString(fmt.Sprintf("%s: %s\r\n", k, v)) + fmt.Fprintf(&msg, "%s: %s\r\n", k, v) } msg.WriteString("\r\n") - msg.WriteString(fmt.Sprintf("--%s\r\n", boundary)) + fmt.Fprintf(&msg, "--%s\r\n", boundary) msg.WriteString("Content-Type: text/plain; charset=utf-8\r\n") msg.WriteString("Content-Transfer-Encoding: quoted-printable\r\n\r\n") textQP := encodeQuotedPrintable(textBody) msg.WriteString(textQP) msg.WriteString("\r\n") - msg.WriteString(fmt.Sprintf("--%s\r\n", boundary)) + fmt.Fprintf(&msg, "--%s\r\n", boundary) msg.WriteString("Content-Type: text/html; charset=utf-8\r\n") msg.WriteString("Content-Transfer-Encoding: quoted-printable\r\n\r\n") htmlQP := encodeQuotedPrintable(htmlBody) msg.WriteString(htmlQP) msg.WriteString("\r\n") - msg.WriteString(fmt.Sprintf("--%s--\r\n", boundary)) + fmt.Fprintf(&msg, "--%s--\r\n", boundary) messageBytes := []byte(msg.String()) diff --git a/main.go b/main.go index b01989d..1a4f5d0 100644 --- a/main.go +++ b/main.go @@ -147,7 +147,8 @@ write to a temp file, and open it in the browser.`, if err := runPreview(cmd.Context(), args[0], outputFile, inline); err != nil { return err } - return exec.Command("open", outputFile).Run() + //nolint:gosec // outputFile is from os.CreateTemp, not user-controlled + return exec.Command("open", outputFile).Run() }, } @@ -203,7 +204,7 @@ func runPreview(ctx context.Context, feedURL, outputFile string, inline bool) er return fmt.Errorf("gmail wrap: %w", err) } - if err := os.WriteFile(outputFile, []byte(gmailOutput), 0644); err != nil { + if err := os.WriteFile(outputFile, []byte(gmailOutput), 0600); err != nil { return fmt.Errorf("write output: %w", err) } logger.Info("preview written", "file", outputFile, "items", len(items)) diff --git a/web/handlers.go b/web/handlers.go index f41159a..1fae52d 100644 --- a/web/handlers.go +++ b/web/handlers.go @@ -506,6 +506,7 @@ func (s *Server) handleConfig(w http.ResponseWriter, r *http.Request, fingerprin } w.Header().Set("Content-Type", "text/plain; charset=utf-8") + //nolint:gosec // text/plain content-type prevents script execution _, _ = w.Write([]byte(cfg.RawText)) } diff --git a/web/server.go b/web/server.go index ca8ac77..dfd3f81 100644 --- a/web/server.go +++ b/web/server.go @@ -71,6 +71,7 @@ func (s *Server) ListenAndServe(ctx context.Context) error { go func() { <-ctx.Done() + //nolint:gosec // Background context needed for graceful shutdown; request ctx is already cancelled _ = srv.Shutdown(context.Background()) }()