From a8d926cea3d5e525a7804e4793ff7cb5855e04cc Mon Sep 17 00:00:00 2001 From: Michael McCarty Date: Thu, 9 Jul 2026 18:42:38 -0700 Subject: [PATCH] fix: replace string concatenation with multiple writes for safety --- src/boost/boost_admin.go | 9 ++++--- src/boost/boost_button_reactions.go | 20 +++++++++++--- src/boost/boost_draw.go | 42 +++++++++++++++++++++-------- src/boost/boost_menu.go | 3 ++- src/boost/leaderboard.go | 9 ++++--- src/boost/predictions_embeded.go | 19 +++++++++---- src/boost/stones.go | 3 ++- src/boost/stones_pages.go | 6 +++-- 8 files changed, 81 insertions(+), 30 deletions(-) diff --git a/src/boost/boost_admin.go b/src/boost/boost_admin.go index d9379de7..b388529f 100644 --- a/src/boost/boost_admin.go +++ b/src/boost/boost_admin.go @@ -404,7 +404,8 @@ func HandleAdminListRoles(s *discordgo.Session, i *discordgo.InteractionCreate) guildRoles, err := s.GuildRoles(i.GuildID) if err != nil { - builder.WriteString("Error retrieving guild roles: " + err.Error()) + builder.WriteString("Error retrieving guild roles: ") + builder.WriteString(err.Error()) } else { for _, c := range ei.EggIncContracts { @@ -591,11 +592,13 @@ func HandleAdminGetContractData(s *discordgo.Session, i *discordgo.InteractionCr jsonData, err := json.Marshal(contract) if err != nil { log.Println(err.Error()) - builder.WriteString("Error formatting JSON data. " + err.Error()) + builder.WriteString("Error formatting JSON data. ") + builder.WriteString(err.Error()) } else { err = json.Indent(buf, jsonData, "", " ") if err != nil { - builder.WriteString("Error formatting JSON data. " + err.Error()) + builder.WriteString("Error formatting JSON data. ") + builder.WriteString(err.Error()) } else { // Create io.Reader from JSON string reader = bytes.NewReader(buf.Bytes()) diff --git a/src/boost/boost_button_reactions.go b/src/boost/boost_button_reactions.go index fd16f36c..56352946 100644 --- a/src/boost/boost_button_reactions.go +++ b/src/boost/boost_button_reactions.go @@ -852,14 +852,26 @@ func buttonReactionHelp(s *discordgo.Session, i *discordgo.InteractionCreate, co outputStr.WriteString("## Boost Bot Icon Meanings\n\n") outputStr.WriteString("See πŸ“Œ message to join the contract.\nSet your number of boost tokens there or ") outputStr.WriteString("add a 4️⃣ to πŸ”Ÿ reaction to the boost list message.\n") - outputStr.WriteString("Active booster reaction of " + boostIcon + " to when spending tokens to boost. Multiple " + boostIcon + " votes by others in the contract will also indicate a boost.\n") - outputStr.WriteString("Use " + contract.TokenStr + " when sending tokens. ") - outputStr.WriteString("During GG use " + ei.GetBotEmojiMarkdown("std_gg") + "/" + ei.GetBotEmojiMarkdown("ultra_gg") + " to send 2 tokens.\n") + outputStr.WriteString("Active booster reaction of ") + outputStr.WriteString(boostIcon) + outputStr.WriteString(" to when spending tokens to boost. Multiple ") + outputStr.WriteString(boostIcon) + outputStr.WriteString(" votes by others in the contract will also indicate a boost.\n") + outputStr.WriteString("Use ") + outputStr.WriteString(contract.TokenStr) + outputStr.WriteString(" when sending tokens. ") + outputStr.WriteString("During GG use ") + outputStr.WriteString(ei.GetBotEmojiMarkdown("std_gg")) + outputStr.WriteString("/") + outputStr.WriteString(ei.GetBotEmojiMarkdown("ultra_gg")) + outputStr.WriteString(" to send 2 tokens.\n") fmt.Fprintf(&outputStr, "Farmer status line, %s:Requested Run, %s:10B Est, %s: Full Hab Est.\n", ei.GetBotEmojiMarkdown("icon_chicken_run"), ei.GetBotEmojiMarkdown("trophy_diamond"), ei.GetBotEmojiMarkdown("hab_full")) //outputStr.WriteString("Active Booster can react with βž• or βž– to adjust number of tokens needed.\n") outputStr.WriteString("Active booster reaction of πŸ”ƒ to exchange position with the next booster.\n") outputStr.WriteString("Reaction of ‡️ to move yourself to last in the current boost order.\n") - outputStr.WriteString("Reaction of " + chickMention + " when you're ready for others to run chickens on your farm.\n") + outputStr.WriteString("Reaction of ") + outputStr.WriteString(chickMention) + outputStr.WriteString(" when you're ready for others to run chickens on your farm.\n") outputStr.WriteString("Anyone can add a 🚽 reaction to express your urgency to boost next.\n") outputStr.WriteString("Additional help through the **/help** command.\n") diff --git a/src/boost/boost_draw.go b/src/boost/boost_draw.go index 1a9ddb6c..8281682b 100644 --- a/src/boost/boost_draw.go +++ b/src/boost/boost_draw.go @@ -612,9 +612,12 @@ func DrawBoostList(s *discordgo.Session, contract *Contract) []discordgo.Message builder.WriteString("\n### Backups\n") for _, userID := range contract.WaitlistBoosters { if bottools.IsValidDiscordID(userID) { - builder.WriteString("<@" + userID + "> ") + builder.WriteString("<@") + builder.WriteString(userID) + builder.WriteString("> ") } else { - builder.WriteString(userID + " ") + builder.WriteString(userID) + builder.WriteString(" ") } } components = append(components, &discordgo.TextDisplay{ @@ -663,11 +666,16 @@ func DrawBoostList(s *discordgo.Session, contract *Contract) []discordgo.Message case ContractStateFastrun: if showTimedGuidance { guidanceStr.WriteString("\n") - guidanceStr.WriteString("> -# Active Booster: " + boostIcon + " when boosting. \n") - guidanceStr.WriteString("> -# Anyone: " + tokenStr + " when sending tokens") - guidanceStr.WriteString(". ❓ Help under Boost Menu.\n") + guidanceStr.WriteString("> -# Active Booster: ") + guidanceStr.WriteString(boostIcon) + guidanceStr.WriteString(" when boosting. \n") + guidanceStr.WriteString("> -# Anyone: ") + guidanceStr.WriteString(tokenStr) + guidanceStr.WriteString(" when sending tokens. ❓ Help under Boost Menu.\n") if contract.CoopSize != len(contract.Order) { - guidanceStr.WriteString("-# > Use pinned message or add πŸ§‘\u200d🌾 reaction to join this list and set boost " + tokenStr + " wanted.\n") + guidanceStr.WriteString("-# > Use pinned message or add πŸ§‘β€πŸŒΎ reaction to join this list and set boost ") + guidanceStr.WriteString(tokenStr) + guidanceStr.WriteString(" wanted.\n") } totalContentLength := guidanceStr.Len() for _, component := range components { @@ -684,17 +692,23 @@ func DrawBoostList(s *discordgo.Session, contract *Contract) []discordgo.Message if showTimedGuidance { if contract.PlayStyle != ContractPlaystyleLeaderboard { guidanceStr.WriteString("\n") - guidanceStr.WriteString("> " + tokenStr + " when sending tokens to the Banker") + guidanceStr.WriteString("> ") + guidanceStr.WriteString(tokenStr) + guidanceStr.WriteString(" when sending tokens to the Banker") runReady, _, _ := ei.GetBotEmoji("runready") guidanceStr.WriteString(".\n") - guidanceStr.WriteString("-# > " + runReady + " when you're ready for others to run chickens on your farm.\n") + guidanceStr.WriteString("-# > ") + guidanceStr.WriteString(runReady) + guidanceStr.WriteString(" when you're ready for others to run chickens on your farm.\n") guidanceStr.WriteString("-# > πŸ’° is used by the Banker to send the requested number of tokens to the booster.\n") guidanceStr.WriteString("-# > -When active Booster is sent tokens by the Banker they are marked as boosted.\n") guidanceStr.WriteString("-# > -Adjust the number of boost tokens you want by adding a 6️⃣ to πŸ”Ÿ reaction to the boost list message.\n") } if contract.CoopSize != len(contract.Order) { - guidanceStr.WriteString("-# > Use pinned message or add πŸ§‘β€πŸŒΎ reaction to join this list and set boost " + tokenStr + " wanted.\n") + guidanceStr.WriteString("-# > Use pinned message or add πŸ§‘β€πŸŒΎ reaction to join this list and set boost ") + guidanceStr.WriteString(tokenStr) + guidanceStr.WriteString(" wanted.\n") } // Sum the Content lengths of the components for this length test // If the length of the builder is less than 1900 characters, add the guidanceStr @@ -720,7 +734,9 @@ func DrawBoostList(s *discordgo.Session, contract *Contract) []discordgo.Message Spacing: &spacing, }) guidanceStr.WriteString("-# > Waiting for other(s) to join...\n") - guidanceStr.WriteString("-# > Use pinned message or add πŸ§‘β€πŸŒΎ reaction to join this list and set boost " + tokenStr + " wanted.\n") + guidanceStr.WriteString("-# > Use pinned message or add πŸ§‘β€πŸŒΎ reaction to join this list and set boost ") + guidanceStr.WriteString(tokenStr) + guidanceStr.WriteString(" wanted.\n") totalContentLength := guidanceStr.Len() for _, component := range components { if textDisplay, ok := component.(*discordgo.TextDisplay); ok { @@ -754,7 +770,11 @@ func DrawBoostList(s *discordgo.Session, contract *Contract) []discordgo.Message if sinkEIName != "" { sinkName += " " + sinkEIName } - builder.WriteString("## Send every " + tokenStr + " to our sink " + sinkName + "\n") + builder.WriteString("## Send every ") + builder.WriteString(tokenStr) + builder.WriteString(" to our sink ") + builder.WriteString(sinkName) + builder.WriteString("\n") } } diff --git a/src/boost/boost_menu.go b/src/boost/boost_menu.go index 31c28a72..6100f861 100644 --- a/src/boost/boost_menu.go +++ b/src/boost/boost_menu.go @@ -290,7 +290,8 @@ func HandleMenuReactions(s *discordgo.Session, i *discordgo.InteractionCreate) { }) currentField.Reset() } - currentField.WriteString(line + "\n") + currentField.WriteString(line) + currentField.WriteString("\n") } if currentField.Len() > 0 { field = append(field, &discordgo.MessageEmbedField{ diff --git a/src/boost/leaderboard.go b/src/boost/leaderboard.go index c3ae080f..2acd8d15 100644 --- a/src/boost/leaderboard.go +++ b/src/boost/leaderboard.go @@ -533,8 +533,10 @@ func leaderboardTable(resp *ei.LeaderboardResponse, season string, guildNames [] bottools.AlignString("Name", nameWidth, bottools.StringAlignLeft), bottools.AlignString("Score", scoreWidth, bottools.StringAlignRight), }, "|") - b.WriteString(header + "\n") - b.WriteString(strings.Repeat("β€”", len(header)) + "\n") + b.WriteString(header) + b.WriteString("\n") + b.WriteString(strings.Repeat("β€”", len(header))) + b.WriteString("\n") for _, r := range rows { row := strings.Join([]string{ @@ -543,7 +545,8 @@ func leaderboardTable(resp *ei.LeaderboardResponse, season string, guildNames [] bottools.AlignString(r.name, nameWidth, bottools.StringAlignLeft), bottools.AlignString(fmt.Sprintf("%.0f", r.score), scoreWidth, bottools.StringAlignRight), }, "|") - b.WriteString(row + "\n") + b.WriteString(row) + b.WriteString("\n") } b.WriteString("```") diff --git a/src/boost/predictions_embeded.go b/src/boost/predictions_embeded.go index 84575bb0..a718b259 100644 --- a/src/boost/predictions_embeded.go +++ b/src/boost/predictions_embeded.go @@ -567,7 +567,8 @@ func getWeeklyEmbeds(wedTime, friTime time.Time, userName, botName, botIconURL s } } if seasonalEmojis.Len() > 0 { - legend.WriteString(seasonalEmojis.String() + " Seasonal LB") + legend.WriteString(seasonalEmojis.String()) + legend.WriteString(" Seasonal LB") } if timeSaverMissing { if legend.Len() > 0 { @@ -576,9 +577,13 @@ func getWeeklyEmbeds(wedTime, friTime time.Time, userName, botName, botIconURL s legend.WriteString("πŸ•―οΈ Missing since") } if legend.Len() > 0 { - footer.WriteString("Legend: " + legend.String() + "\n") + footer.WriteString("Legend: ") + footer.WriteString(legend.String()) + footer.WriteString("\n") } - footer.WriteString(botName + " β€’ /pred weekly β€’ User: " + userName) + footer.WriteString(botName) + footer.WriteString(" β€’ /pred weekly β€’ User: ") + footer.WriteString(userName) var title string if weeklyType == 0 { @@ -627,9 +632,13 @@ func getCollectibleEmbeds(collectibles map[string]collectiblePrediction, userNam legend.WriteString(" Seasonal LB") } if legend.Len() > 0 { - footer.WriteString("Legend: " + legend.String() + "\n") + footer.WriteString("Legend: ") + footer.WriteString(legend.String()) + footer.WriteString("\n") } - footer.WriteString(botName + " β€’ /pred collectibles β€’ User: " + userName) + footer.WriteString(botName) + footer.WriteString(" β€’ /pred collectibles β€’ User: ") + footer.WriteString(userName) return &discordgo.MessageEmbedFooter{Text: footer.String(), IconURL: botIconURL} } diff --git a/src/boost/stones.go b/src/boost/stones.go index fd6558e4..cd4521da 100644 --- a/src/boost/stones.go +++ b/src/boost/stones.go @@ -1231,7 +1231,8 @@ func DownloadCoopStatusStones(contractID string, coopID string, details bool, so if showGlitch { parts = append(parts, "πŸ€₯HabGlitch") } - builder.WriteString(strings.Join(parts, " / ") + "\n") + builder.WriteString(strings.Join(parts, " / ")) + builder.WriteString("\n") } if anyColleggtibles { fmt.Fprintf(&builder, "Colleggtibles show when less than %s\n", strings.Join(colleggtibleStr, ", ")) diff --git a/src/boost/stones_pages.go b/src/boost/stones_pages.go index 5d320722..206c2d36 100644 --- a/src/boost/stones_pages.go +++ b/src/boost/stones_pages.go @@ -55,7 +55,8 @@ func sendStonesPage(s *discordgo.Session, i *discordgo.InteractionCreate, newMes pageBuilder.Reset() currentPageSize = 0 } - pageBuilder.WriteString(line + "\n") + pageBuilder.WriteString(line) + pageBuilder.WriteString("\n") currentPageSize += lineSize } @@ -203,7 +204,8 @@ func sendStonesPage(s *discordgo.Session, i *discordgo.InteractionCreate, newMes }) currentField.Reset() } - currentField.WriteString(line + "\n") + currentField.WriteString(line) + currentField.WriteString("\n") } if currentField.Len() > 0 { field = append(field, &discordgo.MessageEmbedField{