diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7c704feb..ce60596d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,5 @@ name: CI + on: push: branches: @@ -6,29 +7,69 @@ on: pull_request: branches: - master + schedule: + # Run daily at 2 AM UTC to check for new vulnerabilities + - cron: "0 2 * * *" + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: test: + timeout-minutes: 15 strategy: matrix: - go-version: [1.23.x] + go-version: [1.25.x, 1.24.x] os: [ubuntu-latest] runs-on: ${{ matrix.os }} steps: - - uses: actions/setup-go@v3 + - uses: actions/checkout@v6 + with: + persist-credentials: false + + - uses: actions/setup-go@v6 with: go-version: ${{ matrix.go-version }} - - uses: actions/checkout@v3 - - name: Go Lint Standard - uses: golangci/golangci-lint-action@v3 + cache: true + + - name: golangci-lint + uses: golangci/golangci-lint-action@v9 + with: + version: v2.7.2 + + - name: Build + run: go build -v ./... + + - name: Run govulncheck + uses: golang/govulncheck-action@v1 + with: + go-version-input: ${{ matrix.go-version }} + + - name: Test + run: go test -race ./... + + jirabot: + runs-on: ubuntu-latest + defaults: + run: + working-directory: jirabot + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup Node.js + uses: actions/setup-node@v6 with: - version: v1.63 - args: "--out-${NO_FUTURE}format colored-line-number --timeout=15m" - - run: go vet ./... - - run: go test ./... -# Copied from .travis.yml but never migrated to github actions -# - language: node_js -# node_js: 10 -# script: -# - cd jirabot -# - yarn -# - yarn ci + node-version: 25.x + cache: yarn + cache-dependency-path: jirabot/yarn.lock + + - name: Install dependencies + run: yarn install --frozen-lockfile + + - name: Run jirabot CI + run: yarn ci diff --git a/.golangci.yml b/.golangci.yml index 4eb78cae..fc124c57 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,13 +1,65 @@ -linters-settings: - gocritic: - disabled-checks: - - ifElseChain - - elseif +version: "2" + +run: + timeout: 5m + tests: true + +formatters: + enable: + - gofumpt linters: enable: - - gofmt - - gocritic - - unconvert - - revive - - govet + # Core recommended linters + - errcheck # Checks for unchecked errors + - govet # Go vet checks + - ineffassign # Detects ineffectual assignments + - staticcheck # Advanced static analysis + - unused # Finds unused code + + # Code quality + - misspell # Finds commonly misspelled words + - unconvert # Unnecessary type conversions (already enabled in original) + - unparam # Finds unused function parameters + - gocritic # Various checks (already enabled in original) + - revive # Fast, configurable linter (already enabled in original) + + # Security and best practices + - gosec # Security-focused linter + - bodyclose # Checks HTTP response body closed + + settings: + gocritic: + disabled-checks: + - ifElseChain + - elseif + + govet: + enable-all: true + disable: + - shadow + - fieldalignment + + revive: + enable-all-rules: false + + exclusions: + rules: + # Exclude specific revive rules + - linters: + - revive + text: "package-comments" + + - linters: + - revive + text: "exported" + + # Exclude specific staticcheck rules + - linters: + - staticcheck + text: "ST1005" + + # Exclude specific gocritic rules + - linters: + - gocritic + text: "ifElseChain" diff --git a/base/db.go b/base/db.go index 9773c971..57fac37f 100644 --- a/base/db.go +++ b/base/db.go @@ -44,7 +44,7 @@ func NewBaseOAuthDB(db *sql.DB) *BaseOAuthDB { func (d *BaseOAuthDB) GetState(state string) (*OAuthRequest, error) { var oauthState OAuthRequest - row := d.DB.QueryRow(`SELECT identifier, conv_id, msg_id, is_complete + row := d.QueryRow(`SELECT identifier, conv_id, msg_id, is_complete FROM oauth_state WHERE state = ?`, state) err := row.Scan(&oauthState.TokenIdentifier, &oauthState.ConvID, @@ -97,7 +97,7 @@ func NewOAuthDB(db *sql.DB) *OAuthDB { func (d *OAuthDB) GetToken(identifier string) (*oauth2.Token, error) { var token oauth2.Token var expiry int64 - row := d.DB.QueryRow(`SELECT access_token, token_type, refresh_token, ROUND(UNIX_TIMESTAMP(expiry)) + row := d.QueryRow(`SELECT access_token, token_type, refresh_token, ROUND(UNIX_TIMESTAMP(expiry)) FROM oauth WHERE identifier = ?`, identifier) err := row.Scan(&token.AccessToken, &token.TokenType, diff --git a/base/email.go b/base/email.go index 1d70cd5b..6876f1ba 100644 --- a/base/email.go +++ b/base/email.go @@ -12,8 +12,7 @@ type Emailer interface { Send(address, subject, message string) error } -type DummyEmailer struct { -} +type DummyEmailer struct{} func (d DummyEmailer) Send(_, subject, _ string) error { fmt.Printf("subject: %s\n", subject) diff --git a/base/errors.go b/base/errors.go index d779ad35..4a71e821 100644 --- a/base/errors.go +++ b/base/errors.go @@ -1,11 +1,9 @@ package base import ( - "time" - - "runtime/debug" - "os" + "runtime/debug" + "time" "golang.org/x/sync/errgroup" ) diff --git a/base/http.go b/base/http.go index 812a0f88..130a46ba 100644 --- a/base/http.go +++ b/base/http.go @@ -3,6 +3,7 @@ package base import ( "context" "net/http" + "time" ) type HTTPSrv struct { @@ -15,7 +16,10 @@ func NewHTTPSrv(stats *StatsRegistry, debugConfig *ChatDebugOutputConfig) *HTTPS return &HTTPSrv{ DebugOutput: NewDebugOutput("HTTPSrv", debugConfig), Stats: stats.SetPrefix("HTTPSrv"), - srv: &http.Server{Addr: ":8080"}, + srv: &http.Server{ + Addr: ":8080", + ReadHeaderTimeout: 10 * time.Second, + }, } } diff --git a/base/server.go b/base/server.go index f21f6d13..1686e6c7 100644 --- a/base/server.go +++ b/base/server.go @@ -218,9 +218,7 @@ func (s *Server) listenForMsgs(shutdownCh chan struct{}, sub *kbchat.Subscriptio } continue case strings.HasPrefix(cmd, fmt.Sprintf("!%s", feedbackCmd(s.kbc.GetUsername()))): - if err := s.handleFeedback(msg); err != nil { - s.Errorf("listenForMsgs: unable to handleFeedback: %v", err) - } + s.handleFeedback(msg) continue } } @@ -291,7 +289,7 @@ func (s *Server) handleLogSend(msg chat1.MsgSummary) error { } outputBytes, err := io.ReadAll(output) if err != nil { - s.Errorf("unable to read ouput: %v", err) + s.Errorf("unable to read output: %v", err) return err } if len(outputBytes) > 0 { @@ -345,8 +343,8 @@ func (s *Server) handlePProf(msg chat1.MsgSummary) error { // Cleanup after the file is sent. time.Sleep(time.Minute) s.Debug("cleaning up %s", outfile) - if err = os.Remove(outfile); err != nil { - s.Errorf("unable to clean up %s: %v", outfile, err) + if rmErr := os.Remove(outfile); rmErr != nil { + s.Errorf("unable to clean up %s: %v", outfile, rmErr) } }() if _, err := s.kbc.SendAttachmentByConvID(msg.ConvID, outfile, ""); err != nil { @@ -389,7 +387,7 @@ func (s *Server) handleStack(msg chat1.MsgSummary) error { return s.kbfsDebugOutput(msg, stack, "stack") } -func (s *Server) handleFeedback(msg chat1.MsgSummary) error { +func (s *Server) handleFeedback(msg chat1.MsgSummary) { toks := strings.Split(strings.TrimSpace(msg.Content.Text.Body), " ") if len(toks) < 3 { s.ChatEcho(msg.ConvID, "Woah there @%s, I can't deliver a blank message...not again. What did you want to say?", @@ -400,7 +398,6 @@ func (s *Server) handleFeedback(msg chat1.MsgSummary) error { s.ChatEcho(msg.ConvID, "Roger that @%s, passed this along to my humans :robot_face:", msg.Sender.Username) } - return nil } func (s *Server) kbfsDebugOutput(msg chat1.MsgSummary, data []byte, operation string) error { @@ -415,8 +412,12 @@ func (s *Server) kbfsDebugOutput(msg chat1.MsgSummary, data []byte, operation st } fileName := fmt.Sprintf("%s-%d.txt", operation, time.Now().Unix()) filePath := fmt.Sprintf("/tmp/%s", fileName) - defer os.Remove(filePath) - if err := os.WriteFile(filePath, data, 0644); err != nil { + defer func() { + if rmErr := os.Remove(filePath); rmErr != nil { + s.Errorf("unable to clean up %s: %v", filePath, rmErr) + } + }() + if err := os.WriteFile(filePath, data, 0o600); err != nil { return fmt.Errorf("kbfsOutput: failed to write %s output: %s", operation, err) } if err := s.runOptions.Command("fs", "mv", filePath, folder).Run(); err != nil { diff --git a/base/stats.go b/base/stats.go index fdea2fea..6eb2969d 100644 --- a/base/stats.go +++ b/base/stats.go @@ -86,18 +86,18 @@ func (s *StathatBackend) Shutdown() error { func NewStatsBackend(btype StatsBackendType, config interface{}) (StatsBackend, error) { switch btype { case StathatStatsBackendType: - if config, ok := config.(StathatConfig); ok { - reporter := stathat.NewBatchReporter(stathat.DefaultReporter, 200*time.Millisecond) - return &StathatBackend{config: config, reporter: reporter}, nil - } else { + cfg, ok := config.(StathatConfig) + if !ok { return nil, errors.New("invalid stathat config") } + reporter := stathat.NewBatchReporter(stathat.DefaultReporter, 200*time.Millisecond) + return &StathatBackend{config: cfg, reporter: reporter}, nil case DummyStatsBackendType: - if config, ok := config.(*ChatDebugOutputConfig); ok { - return NewDummyStatsBackend(config), nil - } else { + cfg, ok := config.(*ChatDebugOutputConfig) + if !ok { return nil, errors.New("invalid DummyStatsBackend config") } + return NewDummyStatsBackend(cfg), nil default: return nil, errors.New("unknown stats registry type") } @@ -115,11 +115,11 @@ func (r *StatsRegistry) makeFname(name string) string { func (r *StatsRegistry) SetPrefix(prefix string) *StatsRegistry { prefix = r.prefix + prefix + " - " - return newStatsRegistryWithPrefix(r.DebugOutput.Config(), r.backend, prefix) + return newStatsRegistryWithPrefix(r.Config(), r.backend, prefix) } func (r *StatsRegistry) ResetPrefix() *StatsRegistry { - return NewStatsRegistryWithBackend(r.DebugOutput.Config(), r.backend) + return NewStatsRegistryWithBackend(r.Config(), r.backend) } func (r *StatsRegistry) Count(name string) { diff --git a/elastiwatch/elastiwatch/db.go b/elastiwatch/elastiwatch/db.go index 66a2b10a..94f6e1ae 100644 --- a/elastiwatch/elastiwatch/db.go +++ b/elastiwatch/elastiwatch/db.go @@ -2,6 +2,7 @@ package elastiwatch import ( "database/sql" + "fmt" "time" "github.com/keybase/managed-bots/base" @@ -40,7 +41,11 @@ func (d *DB) List() (res []Deferral, err error) { if err != nil { return res, err } - defer rows.Close() + defer func() { + if cerr := rows.Close(); cerr != nil { + fmt.Printf("elastiwatch: failed to close rows: %v\n", cerr) + } + }() for rows.Next() { var def Deferral if err := rows.Scan(&def.ID, &def.Regex, &def.Author, &def.Ctime); err != nil { diff --git a/elastiwatch/elastiwatch/handler.go b/elastiwatch/elastiwatch/handler.go index e7163b1e..7a7feb46 100644 --- a/elastiwatch/elastiwatch/handler.go +++ b/elastiwatch/elastiwatch/handler.go @@ -21,8 +21,7 @@ type Handler struct { var _ base.Handler = (*Handler)(nil) -func NewHandler(kbc *kbchat.API, debugConfig *base.ChatDebugOutputConfig, - httpSrv *HTTPSrv, db *DB, logs *LogWatch) *Handler { +func NewHandler(kbc *kbchat.API, debugConfig *base.ChatDebugOutputConfig, httpSrv *HTTPSrv, db *DB, logs *LogWatch) *Handler { return &Handler{ DebugOutput: base.NewDebugOutput("Handler", debugConfig), kbc: kbc, diff --git a/elastiwatch/elastiwatch/logs.go b/elastiwatch/elastiwatch/logs.go index a4ba2242..2f7c9816 100644 --- a/elastiwatch/elastiwatch/logs.go +++ b/elastiwatch/elastiwatch/logs.go @@ -26,8 +26,7 @@ type LogWatch struct { peekCh chan struct{} } -func NewLogWatch(cli *opensearchapi.Client, db *DB, index, email string, emailer base.Emailer, - alertConvID, emailConvID chat1.ConvIDStr, debugConfig *base.ChatDebugOutputConfig) *LogWatch { +func NewLogWatch(cli *opensearchapi.Client, db *DB, index, email string, emailer base.Emailer, alertConvID, emailConvID chat1.ConvIDStr, debugConfig *base.ChatDebugOutputConfig) *LogWatch { return &LogWatch{ DebugOutput: base.NewDebugOutput("LogWatch", debugConfig), cli: cli, diff --git a/elastiwatch/elastiwatch/render.go b/elastiwatch/elastiwatch/render.go index fd6291e6..bc710b83 100644 --- a/elastiwatch/elastiwatch/render.go +++ b/elastiwatch/elastiwatch/render.go @@ -10,8 +10,7 @@ type renderSection struct { Chunks []chunk } -type htmlRenderer struct { -} +type htmlRenderer struct{} type htmlRenderRow struct { Chunk chunk diff --git a/elastiwatch/main.go b/elastiwatch/main.go index 212c4812..f6580668 100644 --- a/elastiwatch/main.go +++ b/elastiwatch/main.go @@ -50,8 +50,10 @@ func NewBotServer(opts Options) *BotServer { } } -const backs = "```" -const back = "`" +const ( + backs = "```" + back = "`" +) func (s *BotServer) makeAdvertisement() kbchat.Advertisement { deferExtended := fmt.Sprintf(`Defer reporting on logs lines that match the givesn regular expression. Useful if there is a known error spamming emails that is not a problem @@ -114,7 +116,11 @@ func (s *BotServer) Go() (err error) { s.Errorf("failed to connect to MySQL: %s", err) return err } - defer sdb.Close() + defer func() { + if cerr := sdb.Close(); cerr != nil { + fmt.Printf("failed to close DB: %v\n", cerr) + } + }() db := elastiwatch.NewDB(sdb) s.Debug("Connect to Elasticsearch at %s", s.opts.ESAddress) var emailer base.Emailer diff --git a/gcalbot/gcalbot/db.go b/gcalbot/gcalbot/db.go index fbc019ea..d693cb44 100644 --- a/gcalbot/gcalbot/db.go +++ b/gcalbot/gcalbot/db.go @@ -28,7 +28,7 @@ func NewDB( // OAuth state func (d *DB) GetState(state string) (*OAuthRequest, error) { var oauthState OAuthRequest - row := d.DB.QueryRow(` + row := d.QueryRow(` SELECT keybase_username, account_nickname, keybase_conv_id, is_complete FROM oauth_state WHERE state = ? @@ -46,7 +46,7 @@ func (d *DB) GetState(state string) (*OAuthRequest, error) { } func (d *DB) PutState(state string, oauthState OAuthRequest) error { - err := d.RunTxn(func(tx *sql.Tx) error { + return d.RunTxn(func(tx *sql.Tx) error { _, err := tx.Exec(` INSERT INTO oauth_state (state, keybase_username, account_nickname, keybase_conv_id) @@ -58,11 +58,10 @@ func (d *DB) PutState(state string, oauthState OAuthRequest) error { `, state, oauthState.KeybaseUsername, oauthState.AccountNickname, oauthState.KeybaseConvID) return err }) - return err } func (d *DB) CompleteState(state string) error { - err := d.RunTxn(func(tx *sql.Tx) error { + return d.RunTxn(func(tx *sql.Tx) error { _, err := tx.Exec(` UPDATE oauth_state SET is_complete = true @@ -70,7 +69,6 @@ func (d *DB) CompleteState(state string) error { `, state) return err }) - return err } // Account @@ -94,7 +92,7 @@ func (d *DB) InsertAccount(account Account) error { func (d *DB) GetAccount(keybaseUsername, accountNickname string) (account *Account, err error) { account = &Account{} var expiry int64 - row := d.DB.QueryRow(` + row := d.QueryRow(` SELECT keybase_username, account_nickname, access_token, token_type, refresh_token, ROUND(UNIX_TIMESTAMP(expiry)) FROM account WHERE keybase_username = ? AND account_nickname = ? @@ -132,7 +130,7 @@ func (d *DB) DeleteAccount(keybaseUsername, accountNickname string) error { } func (d *DB) ExistsAccount(keybaseUsername string, accountNickname string) (exists bool, err error) { - row := d.DB.QueryRow(` + row := d.QueryRow(` SELECT EXISTS(SELECT * FROM account WHERE keybase_username = ? AND account_nickname = ?) `, keybaseUsername, accountNickname) err = row.Scan(&exists) @@ -140,7 +138,7 @@ func (d *DB) ExistsAccount(keybaseUsername string, accountNickname string) (exis } func (d *DB) GetAccountListForUsername(keybaseUsername string) (accounts []*Account, err error) { - rows, err := d.DB.Query(` + rows, err := d.Query(` SELECT keybase_username, account_nickname, access_token, token_type, refresh_token, ROUND(UNIX_TIMESTAMP(expiry)) FROM account WHERE keybase_username = ? @@ -149,7 +147,11 @@ func (d *DB) GetAccountListForUsername(keybaseUsername string) (accounts []*Acco if err != nil { return nil, err } - defer rows.Close() + defer func() { + if cerr := rows.Close(); cerr != nil { + d.Errorf("GetAccountListForUsername: failed to close rows: %s", cerr) + } + }() for rows.Next() { var account Account var expiry int64 @@ -189,7 +191,7 @@ func (d *DB) UpdateChannel(oldChannelID, newChannelID string, expiry time.Time) } func (d *DB) UpdateChannelNextSyncToken(channelID, nextSyncToken string) error { - return d.DB.RunTxn(func(tx *sql.Tx) error { + return d.RunTxn(func(tx *sql.Tx) error { _, err := tx.Exec(` UPDATE channel SET next_sync_token = ? @@ -202,7 +204,7 @@ func (d *DB) UpdateChannelNextSyncToken(channelID, nextSyncToken string) error { func (d *DB) GetChannel(account *Account, calendarID string) (channel *Channel, err error) { channel = &Channel{} var expiry int64 - row := d.DB.QueryRow(` + row := d.QueryRow(` SELECT channel_id, calendar_id, resource_id, ROUND(UNIX_TIMESTAMP(channel.expiry)), next_sync_token FROM channel WHERE keybase_username = ? AND account_nickname = ? AND calendar_id = ? @@ -224,7 +226,7 @@ func (d *DB) GetChannelAndAccountByID(channelID string) (channel *Channel, accou account = &Account{} var channelExpiry int64 var tokenExpiry int64 - row := d.DB.QueryRow(` + row := d.QueryRow(` SELECT channel_id, calendar_id, resource_id, ROUND(UNIX_TIMESTAMP(channel.expiry)), next_sync_token, account.keybase_username, account.account_nickname, access_token, token_type, refresh_token, ROUND(UNIX_TIMESTAMP(account.expiry)) @@ -248,7 +250,7 @@ func (d *DB) GetChannelAndAccountByID(channelID string) (channel *Channel, accou } func (d *DB) GetChannelListByAccount(account *Account) (channels []*Channel, err error) { - rows, err := d.DB.Query(` + rows, err := d.Query(` SELECT channel_id, calendar_id, resource_id, ROUND(UNIX_TIMESTAMP(expiry)), next_sync_token FROM channel WHERE keybase_username = ? AND account_nickname = ? @@ -256,7 +258,9 @@ func (d *DB) GetChannelListByAccount(account *Account) (channels []*Channel, err if err != nil { return nil, err } - defer rows.Close() + defer func() { + _ = rows.Close() + }() for rows.Next() { var channel Channel var expiry int64 @@ -272,7 +276,7 @@ func (d *DB) GetChannelListByAccount(account *Account) (channels []*Channel, err func (d *DB) GetExpiringChannelAndAccountList() (pairs []*ChannelAndAccount, err error) { // query all channels that are expiring in less than a day - rows, err := d.DB.Query(` + rows, err := d.Query(` SELECT channel_id, calendar_id, resource_id, ROUND(UNIX_TIMESTAMP(channel.expiry)), next_sync_token, account.keybase_username, account.account_nickname, access_token, token_type, refresh_token, ROUND(UNIX_TIMESTAMP(account.expiry)) @@ -283,7 +287,9 @@ func (d *DB) GetExpiringChannelAndAccountList() (pairs []*ChannelAndAccount, err if err != nil { return nil, err } - defer rows.Close() + defer func() { + _ = rows.Close() + }() for rows.Next() { var pair ChannelAndAccount var channelExpiry int64 @@ -303,7 +309,7 @@ func (d *DB) GetExpiringChannelAndAccountList() (pairs []*ChannelAndAccount, err } func (d *DB) ExistsChannelByAccountAndCalendar(account *Account, calendarID string) (exists bool, err error) { - row := d.DB.QueryRow(` + row := d.QueryRow(` SELECT EXISTS(SELECT * FROM channel WHERE keybase_username = ? AND account_nickname = ? AND calendar_id = ?) `, account.KeybaseUsername, account.AccountNickname, calendarID) err = row.Scan(&exists) @@ -336,7 +342,7 @@ func (d *DB) InsertSubscription(account *Account, subscription Subscription) err func (d *DB) ExistsSubscription(account *Account, subscription Subscription) (exists bool, err error) { minutesBefore := GetMinutesFromDuration(subscription.DurationBefore) - row := d.DB.QueryRow(` + row := d.QueryRow(` SELECT EXISTS( SELECT * FROM subscription @@ -350,7 +356,7 @@ func (d *DB) ExistsSubscription(account *Account, subscription Subscription) (ex } func (d *DB) CountSubscriptionsByAccountAndCalender(account *Account, calendarID string) (count int, err error) { - row := d.DB.QueryRow(` + row := d.QueryRow(` SELECT COUNT(*) FROM subscription WHERE keybase_username = ? AND account_nickname = ? AND calendar_id = ? `, account.KeybaseUsername, account.AccountNickname, calendarID) err = row.Scan(&count) @@ -358,7 +364,7 @@ func (d *DB) CountSubscriptionsByAccountAndCalender(account *Account, calendarID } func (d *DB) GetReminderSubscriptionAndAccountPairs() (pairs []*SubscriptionAndAccount, err error) { - row, err := d.DB.Query(` + row, err := d.Query(` SELECT calendar_id, keybase_conv_id, minutes_before, type, -- subscription account.keybase_username, account.account_nickname, access_token, token_type, refresh_token, ROUND(UNIX_TIMESTAMP(expiry)) -- account @@ -369,7 +375,11 @@ func (d *DB) GetReminderSubscriptionAndAccountPairs() (pairs []*SubscriptionAndA if err != nil { return nil, err } - defer row.Close() + defer func() { + if cerr := row.Close(); cerr != nil { + d.Errorf("GetReminderSubscriptionAndAccountPairs: failed to close rows: %s", cerr) + } + }() for row.Next() { var pair SubscriptionAndAccount var subscriptionMinutesBefore int @@ -392,7 +402,7 @@ func (d *DB) GetReminderSubscriptionsByAccountAndCalendar( calendarID string, subscriptionType SubscriptionType, ) (subscriptions []*Subscription, err error) { - row, err := d.DB.Query(` + row, err := d.Query(` SELECT calendar_id, keybase_conv_id, minutes_before, type FROM subscription WHERE keybase_username = ? AND account_nickname = ? AND calendar_id = ? AND type = ? @@ -400,7 +410,11 @@ func (d *DB) GetReminderSubscriptionsByAccountAndCalendar( if err != nil { return nil, err } - defer row.Close() + defer func() { + if cerr := row.Close(); cerr != nil { + d.Errorf("GetReminderSubscriptionsByAccountAndCalendar: failed to close rows: %s", cerr) + } + }() for row.Next() { var subscription Subscription var minutesBefore int @@ -415,7 +429,7 @@ func (d *DB) GetReminderSubscriptionsByAccountAndCalendar( } func (d *DB) GetSubscriptions(account *Account, calendarID string, keybaseConvID chat1.ConvIDStr) (subscriptions []*Subscription, err error) { - rows, err := d.DB.Query(` + rows, err := d.Query(` SELECT calendar_id, keybase_conv_id, minutes_before, type FROM subscription WHERE keybase_username = ? AND account_nickname = ? AND calendar_id = ? AND keybase_conv_id = ? @@ -423,7 +437,11 @@ func (d *DB) GetSubscriptions(account *Account, calendarID string, keybaseConvID if err != nil { return nil, err } - defer rows.Close() + defer func() { + if cerr := rows.Close(); cerr != nil { + d.Errorf("GetSubscriptions: failed to close rows: %s", cerr) + } + }() for rows.Next() { var subscription Subscription var minutesBefore int @@ -465,7 +483,7 @@ func (d *DB) InsertInvite(account *Account, invite Invite) error { } func (d *DB) ExistsInvite(account *Account, calendarID, eventID string) (exists bool, err error) { - row := d.DB.QueryRow(` + row := d.QueryRow(` SELECT EXISTS( SELECT * FROM invite WHERE keybase_username = ? AND account_nickname = ? AND calendar_id = ? AND event_id = ? ) @@ -478,7 +496,7 @@ func (d *DB) GetInviteAndAccountByUserMessage(keybaseUsername string, messageID invite = &Invite{} account = &Account{} var expiry int64 - row := d.DB.QueryRow(` + row := d.QueryRow(` SELECT calendar_id, event_id, message_id, account.keybase_username, account.account_nickname, access_token, token_type, refresh_token, ROUND(UNIX_TIMESTAMP(expiry)) @@ -520,7 +538,7 @@ func (d *DB) InsertDailyScheduleSubscription(account *Account, subscription Dail } func (d *DB) GetAggregatedDailyScheduleSubscription(scheduleToSend ScheduleToSendType) (subscriptions []*AggregatedDailyScheduleSubscription, err error) { - row, err := d.DB.Query(` + row, err := d.Query(` SELECT GROUP_CONCAT(calendar_id) as calendar_ids, keybase_conv_id, timezone, days_to_send, schedule_to_send, notification_time, account.keybase_username, account.account_nickname, access_token, token_type, refresh_token, ROUND(UNIX_TIMESTAMP(expiry)) @@ -532,7 +550,11 @@ func (d *DB) GetAggregatedDailyScheduleSubscription(scheduleToSend ScheduleToSen if err != nil { return nil, err } - defer row.Close() + defer func() { + if cerr := row.Close(); cerr != nil { + d.Errorf("GetAggregatedDailyScheduleSubscription: failed to close rows: %s", cerr) + } + }() for row.Next() { var pair AggregatedDailyScheduleSubscription var concatCalendarIDs string @@ -570,7 +592,7 @@ func (d *DB) GetDailyScheduleSubscription( subscription = &DailyScheduleSubscription{} var timezone string var notificationTime string - row := d.DB.QueryRow(` + row := d.QueryRow(` SELECT calendar_id, keybase_conv_id, timezone, days_to_send, schedule_to_send, notification_time FROM daily_schedule_subscription WHERE keybase_username = ? AND account_nickname = ? AND calendar_id = ? AND keybase_conv_id = ? diff --git a/gcalbot/gcalbot/http.go b/gcalbot/gcalbot/http.go index ceb62a76..3575ca73 100644 --- a/gcalbot/gcalbot/http.go +++ b/gcalbot/gcalbot/http.go @@ -260,7 +260,7 @@ func (h *HTTPSrv) configHandler(w http.ResponseWriter, r *http.Request) { return } // get list of times in half hour increments - baseTime := time.Date(2006, 01, 02, 0, 0, 0, 0, dsTimezone) + baseTime := time.Date(2006, time.January, 2, 0, 0, 0, 0, dsTimezone) for i := 0; i < 48; i++ { var title string minutes := i * 30 @@ -378,8 +378,7 @@ func (h *HTTPSrv) configHandler(w http.ResponseWriter, r *http.Request) { } else if !page.Invite && invite { // create invite subscription h.Stats.Count("config - update - invite - create") - _, err = h.handler.createSubscription(selectedAccount, inviteSubscription) - if err != nil { + if err = h.handler.createSubscription(selectedAccount, inviteSubscription); err != nil { return } } @@ -416,7 +415,7 @@ func (h *HTTPSrv) configHandler(w http.ResponseWriter, r *http.Request) { return } - _, err = h.handler.createSubscription(selectedAccount, Subscription{ + err = h.handler.createSubscription(selectedAccount, Subscription{ CalendarID: calendarID, KeybaseConvID: keybaseConvID, DurationBefore: GetDurationFromMinutes(newMinutesBefore), diff --git a/gcalbot/gcalbot/oauth.go b/gcalbot/gcalbot/oauth.go index 6ee9434d..a4f7a671 100644 --- a/gcalbot/gcalbot/oauth.go +++ b/gcalbot/gcalbot/oauth.go @@ -95,7 +95,7 @@ func (h *HTTPSrv) oauthHandler(w http.ResponseWriter, r *http.Request) { return } - _, err = h.handler.createSubscription(&account, Subscription{ + err = h.handler.createSubscription(&account, Subscription{ CalendarID: primaryCalendar.Id, KeybaseConvID: req.KeybaseConvID, Type: SubscriptionTypeInvite, @@ -103,7 +103,7 @@ func (h *HTTPSrv) oauthHandler(w http.ResponseWriter, r *http.Request) { if err != nil { return } - _, err = h.handler.createSubscription(&account, Subscription{ + err = h.handler.createSubscription(&account, Subscription{ CalendarID: primaryCalendar.Id, KeybaseConvID: req.KeybaseConvID, DurationBefore: GetDurationFromMinutes(5), diff --git a/gcalbot/gcalbot/schedulescheduler/send.go b/gcalbot/gcalbot/schedulescheduler/send.go index 36edd953..fc25c234 100644 --- a/gcalbot/gcalbot/schedulescheduler/send.go +++ b/gcalbot/gcalbot/schedulescheduler/send.go @@ -146,7 +146,6 @@ func (s *ScheduleScheduler) SendDailyScheduleMessage(sendMinute time.Time, subsc events = append(events, page.Items...) return nil }) - if err != nil { s.Debug("error getting events from API: %s", err) continue diff --git a/gcalbot/gcalbot/webhook.go b/gcalbot/gcalbot/webhook.go index f16d5955..3f9732df 100644 --- a/gcalbot/gcalbot/webhook.go +++ b/gcalbot/gcalbot/webhook.go @@ -203,26 +203,24 @@ func (h *HTTPSrv) handleEventUpdateWebhook(w http.ResponseWriter, r *http.Reques func (h *Handler) createSubscription( account *Account, subscription Subscription, -) (exists bool, err error) { - exists, err = h.db.ExistsSubscription(account, subscription) +) error { + exists, err := h.db.ExistsSubscription(account, subscription) if err != nil || exists { // if no error, subscription exists, short circuit - return exists, err + return err } - err = h.createEventChannel(account, subscription.CalendarID) - if err != nil { - return exists, err + if err := h.createEventChannel(account, subscription.CalendarID); err != nil { + return err } - err = h.db.InsertSubscription(account, subscription) - if err != nil { - return exists, err + if err := h.db.InsertSubscription(account, subscription); err != nil { + return err } h.reminderScheduler.AddSubscription(account, subscription) - return false, nil + return nil } func (h *Handler) removeSubscription( @@ -311,7 +309,6 @@ func (h *Handler) createEventChannel(account *Account, calendarID string) error ResourceID: res.ResourceId, Expiry: time.Unix(res.Expiration/1e3, 0), }) - if err != nil { return err } diff --git a/gcalbot/main.go b/gcalbot/main.go index 2f51b098..2e9d29a8 100644 --- a/gcalbot/main.go +++ b/gcalbot/main.go @@ -54,11 +54,12 @@ func NewBotServer(opts Options) *BotServer { } } -const back = "`" -const backs = "```" +const ( + back = "`" + backs = "```" +) func (s *BotServer) makeAdvertisement() kbchat.Advertisement { - accountsConnectDesc := fmt.Sprintf(`Connects a Google account to the Google Calendar bot and stores the connection under a descriptive nickname. View your connected Google accounts using %s!gcal accounts list%s @@ -207,7 +208,11 @@ func (s *BotServer) Go() (err error) { s.Errorf("failed to connect to MySQL: %s", err) return err } - defer sdb.Close() + defer func() { + if cerr := sdb.Close(); cerr != nil { + fmt.Printf("failed to close DB: %v\n", cerr) + } + }() db := gcalbot.NewDB(sdb, debugConfig) stats = stats.SetPrefix(s.Name()) diff --git a/githubbot/githubbot/db.go b/githubbot/githubbot/db.go index c84a348e..ef416773 100644 --- a/githubbot/githubbot/db.go +++ b/githubbot/githubbot/db.go @@ -2,6 +2,7 @@ package githubbot import ( "database/sql" + "fmt" "strings" "github.com/keybase/go-keybase-chat-bot/kbchat/types/chat1" @@ -88,7 +89,7 @@ func (d *DB) DeleteBranchesForRepo(convID chat1.ConvIDStr, repo string) error { } func (d *DB) GetConvIDsFromRepoInstallation(repo string, installationID int64) (res []chat1.ConvIDStr, err error) { - rows, err := d.DB.Query(` + rows, err := d.Query(` SELECT conv_id FROM subscriptions WHERE repo = ? AND installation_id = ? @@ -97,7 +98,11 @@ func (d *DB) GetConvIDsFromRepoInstallation(repo string, installationID int64) ( if err != nil { return res, err } - defer rows.Close() + defer func() { + if cerr := rows.Close(); cerr != nil { + fmt.Printf("GetConvIDsFromRepoInstallation: failed to close rows: %v\n", cerr) + } + }() for rows.Next() { var convID chat1.ConvIDStr if err := rows.Scan(&convID); err != nil { @@ -109,7 +114,7 @@ func (d *DB) GetConvIDsFromRepoInstallation(repo string, installationID int64) ( } func (d *DB) GetSubscriptionForBranchExists(convID chat1.ConvIDStr, repo string, branch string) (exists bool, err error) { - row := d.DB.QueryRow(` + row := d.QueryRow(` SELECT 1 FROM branches WHERE conv_id = ? AND repo = ? AND branch = ? @@ -128,7 +133,7 @@ func (d *DB) GetSubscriptionForBranchExists(convID chat1.ConvIDStr, repo string, } func (d *DB) GetSubscriptionForRepoExists(convID chat1.ConvIDStr, repo string) (exists bool, err error) { - row := d.DB.QueryRow(` + row := d.QueryRow(` SELECT 1 FROM subscriptions WHERE conv_id = ? AND repo = ? @@ -146,14 +151,16 @@ func (d *DB) GetSubscriptionForRepoExists(convID chat1.ConvIDStr, repo string) ( } func (d *DB) GetAllBranchesForRepo(convID chat1.ConvIDStr, repo string) ([]string, error) { - rows, err := d.DB.Query(`SELECT branch + rows, err := d.Query(`SELECT branch FROM branches WHERE conv_id = ? AND repo = ?`, convID, repo) if err != nil { return nil, err } res := []string{} - defer rows.Close() + defer func() { + _ = rows.Close() + }() for rows.Next() { var branch string if err := rows.Scan(&branch); err != nil { @@ -221,7 +228,7 @@ func (d *DB) SetFeatures(convID chat1.ConvIDStr, repo string, features *Features } func (d *DB) GetFeatures(convID chat1.ConvIDStr, repo string) (*Features, error) { - row := d.DB.QueryRow(`SELECT issues, pull_requests, commits, statuses, releases + row := d.QueryRow(`SELECT issues, pull_requests, commits, statuses, releases FROM features WHERE conv_id = ? AND repo = ?`, convID, repo) features := &Features{} @@ -237,7 +244,7 @@ func (d *DB) GetFeatures(convID chat1.ConvIDStr, repo string) (*Features, error) } func (d *DB) GetFeaturesForAllRepos(convID chat1.ConvIDStr) (map[string]Features, error) { - rows, err := d.DB.Query(`SELECT repo, COALESCE(issues, true), COALESCE(pull_requests, true), + rows, err := d.Query(`SELECT repo, COALESCE(issues, true), COALESCE(pull_requests, true), COALESCE(commits, true), COALESCE(statuses, true), COALESCE(releases, true) FROM subscriptions LEFT JOIN features USING(conv_id, repo) @@ -246,7 +253,9 @@ func (d *DB) GetFeaturesForAllRepos(convID chat1.ConvIDStr) (map[string]Features return nil, err } res := make(map[string]Features) - defer rows.Close() + defer func() { + _ = rows.Close() + }() for rows.Next() { var repo string var features Features @@ -272,7 +281,7 @@ func (d *DB) DeleteFeaturesForRepo(convID chat1.ConvIDStr, repo string) error { func (d *DB) GetToken(identifier string) (*oauth2.Token, error) { var token oauth2.Token - row := d.DB.QueryRow(`SELECT access_token, token_type + row := d.QueryRow(`SELECT access_token, token_type FROM oauth WHERE identifier = ?`, identifier) err := row.Scan(&token.AccessToken, &token.TokenType) @@ -315,7 +324,7 @@ type UserPreferences struct { } func (d *DB) GetUserPreferences(username string, convID chat1.ConvIDStr) (*UserPreferences, error) { - row := d.DB.QueryRow(`SELECT mention + row := d.QueryRow(`SELECT mention FROM user_prefs WHERE username = ? AND conv_id = ?`, username, convID) prefs := &UserPreferences{} @@ -354,14 +363,16 @@ type DBSubscription struct { } func (d *DB) GetAllSubscriptions() (res []DBSubscription, err error) { - rows, err := d.DB.Query(` + rows, err := d.Query(` SELECT conv_id, repo, installation_id FROM subscriptions `) if err != nil { return res, err } - defer rows.Close() + defer func() { + _ = rows.Close() + }() for rows.Next() { var subscription DBSubscription if err := rows.Scan(&subscription.ConvID, &subscription.Repo, &subscription.InstallationID); err != nil { diff --git a/githubbot/githubbot/handler.go b/githubbot/githubbot/handler.go index 786ee83a..f2c5176a 100644 --- a/githubbot/githubbot/handler.go +++ b/githubbot/githubbot/handler.go @@ -6,7 +6,7 @@ import ( "net/http" "strings" - "github.com/bradleyfalzon/ghinstallation" + "github.com/bradleyfalzon/ghinstallation/v2" "github.com/google/go-github/v31/github" "github.com/keybase/go-keybase-chat-bot/kbchat" @@ -29,9 +29,7 @@ type Handler struct { var _ base.Handler = (*Handler)(nil) -func NewHandler(stats *base.StatsRegistry, kbc *kbchat.API, debugConfig *base.ChatDebugOutputConfig, db *DB, - oauthConfig *oauth2.Config, atr *ghinstallation.AppsTransport, - httpPrefix, appName string) *Handler { +func NewHandler(stats *base.StatsRegistry, kbc *kbchat.API, debugConfig *base.ChatDebugOutputConfig, db *DB, oauthConfig *oauth2.Config, atr *ghinstallation.AppsTransport, httpPrefix, appName string) *Handler { return &Handler{ DebugOutput: base.NewDebugOutput("Handler", debugConfig), stats: stats.SetPrefix("Handler"), diff --git a/githubbot/githubbot/http.go b/githubbot/githubbot/http.go index 1e726500..abcb988d 100644 --- a/githubbot/githubbot/http.go +++ b/githubbot/githubbot/http.go @@ -10,7 +10,7 @@ import ( "github.com/keybase/managed-bots/base/git" - "github.com/bradleyfalzon/ghinstallation" + "github.com/bradleyfalzon/ghinstallation/v2" "github.com/google/go-github/v31/github" "github.com/keybase/go-keybase-chat-bot/kbchat" @@ -28,8 +28,7 @@ type HTTPSrv struct { secret string } -func NewHTTPSrv(stats *base.StatsRegistry, kbc *kbchat.API, debugConfig *base.ChatDebugOutputConfig, db *DB, handler *Handler, - oauthConfig *oauth2.Config, atr *ghinstallation.AppsTransport, secret string) *HTTPSrv { +func NewHTTPSrv(stats *base.StatsRegistry, kbc *kbchat.API, debugConfig *base.ChatDebugOutputConfig, db *DB, handler *Handler, oauthConfig *oauth2.Config, atr *ghinstallation.AppsTransport, secret string) *HTTPSrv { h := &HTTPSrv{ kbc: kbc, db: db, @@ -45,7 +44,9 @@ func NewHTTPSrv(stats *base.StatsRegistry, kbc *kbchat.API, debugConfig *base.Ch } func (h *HTTPSrv) handleHealthCheck(w http.ResponseWriter, _ *http.Request) { - fmt.Fprintf(w, "beep boop! :)") + if _, err := fmt.Fprintf(w, "beep boop! :)"); err != nil { + h.Debug("handleHealthCheck: failed to write response: %s", err) + } } func (h *HTTPSrv) handleWebhook(_ http.ResponseWriter, r *http.Request) { diff --git a/githubbot/githubbot/util.go b/githubbot/githubbot/util.go index 308dfbfa..78b16571 100644 --- a/githubbot/githubbot/util.go +++ b/githubbot/githubbot/util.go @@ -16,7 +16,7 @@ import ( ) func getCommitMessages(event *github.PushEvent) []string { - var commitMsgs = make([]string, 0) + commitMsgs := make([]string, 0) for _, commit := range event.Commits { commitMsgs = append(commitMsgs, commit.GetMessage()) } diff --git a/githubbot/main.go b/githubbot/main.go index 199993ab..fd3bddb6 100644 --- a/githubbot/main.go +++ b/githubbot/main.go @@ -12,7 +12,7 @@ import ( _ "github.com/go-sql-driver/mysql" - "github.com/bradleyfalzon/ghinstallation" + "github.com/bradleyfalzon/ghinstallation/v2" "github.com/keybase/go-keybase-chat-bot/kbchat" "github.com/keybase/go-keybase-chat-bot/kbchat/types/chat1" "github.com/keybase/managed-bots/base" @@ -142,7 +142,11 @@ func (s *BotServer) getAppKey() (appKey []byte, err error) { if err != nil { return nil, err } - defer keyFile.Close() + defer func() { + if cerr := keyFile.Close(); cerr != nil { + s.Errorf("getAppKey: failed to close key file: %v", cerr) + } + }() b, err := io.ReadAll(keyFile) if err != nil { @@ -208,7 +212,11 @@ func (s *BotServer) Go() (err error) { s.Errorf("failed to connect to MySQL: %s", err) return err } - defer sdb.Close() + defer func() { + if cerr := sdb.Close(); cerr != nil { + s.Errorf("Go: failed to close DB: %v", cerr) + } + }() db := githubbot.NewDB(sdb) botConfig, err := s.getConfig() diff --git a/githubbot/migrations/default_branch.go b/githubbot/migrations/default_branch.go index f3c490c2..926e86c1 100644 --- a/githubbot/migrations/default_branch.go +++ b/githubbot/migrations/default_branch.go @@ -8,8 +8,9 @@ import ( "io" "net/http" "os" + "path/filepath" - "github.com/bradleyfalzon/ghinstallation" + "github.com/bradleyfalzon/ghinstallation/v2" _ "github.com/go-sql-driver/mysql" "github.com/google/go-github/v31/github" @@ -22,11 +23,14 @@ func main() { } func getAppKey(privateKeyPath string) ([]byte, error) { - keyFile, err := os.Open(privateKeyPath) + cleanPath := filepath.Clean(privateKeyPath) + keyFile, err := os.Open(cleanPath) if err != nil { return []byte{}, err } - defer keyFile.Close() + defer func() { + _ = keyFile.Close() + }() b, err := io.ReadAll(keyFile) if err != nil { @@ -63,7 +67,9 @@ func mainInner() int { fmt.Printf("failed to connect to MySQL: %s", err) return 1 } - defer sdb.Close() + defer func() { + _ = sdb.Close() + }() db := githubbot.NewDB(sdb) tr := http.DefaultTransport diff --git a/gitlabbot/gitlabbot/db.go b/gitlabbot/gitlabbot/db.go index 3d163af8..42cb481b 100644 --- a/gitlabbot/gitlabbot/db.go +++ b/gitlabbot/gitlabbot/db.go @@ -2,6 +2,7 @@ package gitlabbot import ( "database/sql" + "fmt" "github.com/keybase/go-keybase-chat-bot/kbchat/types/chat1" @@ -55,7 +56,7 @@ func (d *DB) DeleteSubscriptionsForRepo(convID chat1.ConvIDStr, repo string) err } func (d *DB) GetSubscribedConvs(repo string) (res []chat1.ConvIDStr, err error) { - rows, err := d.DB.Query(` + rows, err := d.Query(` SELECT conv_id FROM subscriptions WHERE repo = ? @@ -64,7 +65,11 @@ func (d *DB) GetSubscribedConvs(repo string) (res []chat1.ConvIDStr, err error) if err != nil { return res, err } - defer rows.Close() + defer func() { + if cerr := rows.Close(); cerr != nil { + fmt.Printf("GetSubscribedConvs: failed to close rows: %v\n", cerr) + } + }() for rows.Next() { var convID chat1.ConvIDStr if err := rows.Scan(&convID); err != nil { @@ -76,7 +81,7 @@ func (d *DB) GetSubscribedConvs(repo string) (res []chat1.ConvIDStr, err error) } func (d *DB) GetSubscriptionExists(convID chat1.ConvIDStr, repo string) (exists bool, err error) { - row := d.DB.QueryRow(` + row := d.QueryRow(` SELECT 1 FROM subscriptions WHERE (conv_id = ? AND repo = ?) @@ -95,7 +100,7 @@ func (d *DB) GetSubscriptionExists(convID chat1.ConvIDStr, repo string) (exists } func (d *DB) GetSubscriptionForRepoExists(convID chat1.ConvIDStr, repo string) (exists bool, err error) { - row := d.DB.QueryRow(` + row := d.QueryRow(` SELECT 1 FROM subscriptions WHERE (conv_id = ? AND repo = ?) @@ -113,7 +118,7 @@ func (d *DB) GetSubscriptionForRepoExists(convID chat1.ConvIDStr, repo string) ( } func (d *DB) GetAllSubscriptionsForConvID(convID chat1.ConvIDStr) (res []string, err error) { - rows, err := d.DB.Query(` + rows, err := d.Query(` SELECT repo FROM subscriptions WHERE conv_id = ? @@ -122,7 +127,11 @@ func (d *DB) GetAllSubscriptionsForConvID(convID chat1.ConvIDStr) (res []string, if err != nil { return nil, err } - defer rows.Close() + defer func() { + if cerr := rows.Close(); cerr != nil { + fmt.Printf("GetAllSubscriptionsForConvID: failed to close rows: %v\n", cerr) + } + }() for rows.Next() { var repo string if err := rows.Scan(&repo); err != nil { @@ -137,7 +146,7 @@ func (d *DB) GetAllSubscriptionsForConvID(convID chat1.ConvIDStr) (res []string, func (d *DB) GetToken(identifier string) (*oauth2.Token, error) { var token oauth2.Token - row := d.DB.QueryRow(`SELECT access_token, token_type + row := d.QueryRow(`SELECT access_token, token_type FROM oauth WHERE identifier = ?`, identifier) err := row.Scan(&token.AccessToken, &token.TokenType) diff --git a/gitlabbot/gitlabbot/handler.go b/gitlabbot/gitlabbot/handler.go index 190b32ff..04650385 100644 --- a/gitlabbot/gitlabbot/handler.go +++ b/gitlabbot/gitlabbot/handler.go @@ -21,8 +21,14 @@ type Handler struct { var _ base.Handler = (*Handler)(nil) -func NewHandler(stats *base.StatsRegistry, kbc *kbchat.API, debugConfig *base.ChatDebugOutputConfig, - db *DB, httpPrefix string, secret string) *Handler { +func NewHandler( + stats *base.StatsRegistry, + kbc *kbchat.API, + debugConfig *base.ChatDebugOutputConfig, + db *DB, + httpPrefix string, + secret string, +) *Handler { return &Handler{ DebugOutput: base.NewDebugOutput("Handler", debugConfig), stats: stats.SetPrefix("Handler"), diff --git a/gitlabbot/gitlabbot/http.go b/gitlabbot/gitlabbot/http.go index b0c61b16..c3fd47ea 100644 --- a/gitlabbot/gitlabbot/http.go +++ b/gitlabbot/gitlabbot/http.go @@ -22,8 +22,14 @@ type HTTPSrv struct { secret string } -func NewHTTPSrv(stats *base.StatsRegistry, kbc *kbchat.API, debugConfig *base.ChatDebugOutputConfig, - db *DB, handler *Handler, secret string) *HTTPSrv { +func NewHTTPSrv( + stats *base.StatsRegistry, + kbc *kbchat.API, + debugConfig *base.ChatDebugOutputConfig, + db *DB, + handler *Handler, + secret string, +) *HTTPSrv { h := &HTTPSrv{ kbc: kbc, db: db, @@ -37,7 +43,9 @@ func NewHTTPSrv(stats *base.StatsRegistry, kbc *kbchat.API, debugConfig *base.Ch } func (h *HTTPSrv) handleHealthCheck(w http.ResponseWriter, _ *http.Request) { - fmt.Fprintf(w, "beep boop! :)") + if _, err := fmt.Fprintf(w, "beep boop! :)"); err != nil { + h.Debug("handleHealthCheck: failed to write response: %s", err) + } } func (h *HTTPSrv) handleWebhook(_ http.ResponseWriter, r *http.Request) { @@ -46,7 +54,11 @@ func (h *HTTPSrv) handleWebhook(_ http.ResponseWriter, r *http.Request) { h.Errorf("Error reading payload: %s", err) return } - defer r.Body.Close() + defer func() { + if cerr := r.Body.Close(); cerr != nil { + h.Errorf("handleWebhook: failed to close request body: %s", cerr) + } + }() event, err := gitlab.ParseWebhook(gitlab.WebhookEventType(r), payload) if err != nil { @@ -112,7 +124,7 @@ func (h *HTTPSrv) handleWebhook(_ http.ResponseWriter, r *http.Request) { } for _, convID := range convs { - var secretToken = base.MakeSecret(repo, convID, h.secret) + secretToken := base.MakeSecret(repo, convID, h.secret) if signature != secretToken { h.Debug("Error validating payload signature for conversation %s: %v", convID, err) continue diff --git a/gitlabbot/gitlabbot/util.go b/gitlabbot/gitlabbot/util.go index 78d63fcc..cb5fdaa9 100644 --- a/gitlabbot/gitlabbot/util.go +++ b/gitlabbot/gitlabbot/util.go @@ -15,7 +15,7 @@ import ( var repoRegex = regexp.MustCompile(`^[a-zA-Z0-9_\.-]*$`) func getCommitMessages(event *gitlab.PushEvent) []string { - var commitMsgs = make([]string, 0) + commitMsgs := make([]string, 0, len(event.Commits)) for _, commit := range event.Commits { commitMsgs = append(commitMsgs, commit.Message) } diff --git a/gitlabbot/main.go b/gitlabbot/main.go index 9e8dab8a..e58c2ba7 100644 --- a/gitlabbot/main.go +++ b/gitlabbot/main.go @@ -137,7 +137,11 @@ func (s *BotServer) Go() (err error) { s.Errorf("failed to connect to MySQL: %s", err) return err } - defer sdb.Close() + defer func() { + if cerr := sdb.Close(); cerr != nil { + fmt.Printf("failed to close DB: %v\n", cerr) + } + }() db := gitlabbot.NewDB(sdb) debugConfig := base.NewChatDebugOutputConfig(s.kbc, s.opts.ErrReportConv) diff --git a/go.mod b/go.mod index 10440021..e481ef1a 100644 --- a/go.mod +++ b/go.mod @@ -1,41 +1,41 @@ module github.com/keybase/managed-bots -go 1.23 +go 1.24.0 -toolchain go1.23.4 +toolchain go1.25.5 require ( github.com/aws/aws-sdk-go v1.55.7 - github.com/bradleyfalzon/ghinstallation v1.1.0 - github.com/go-sql-driver/mysql v1.8.1 + github.com/bradleyfalzon/ghinstallation/v2 v2.17.0 + github.com/go-sql-driver/mysql v1.9.3 github.com/google/go-github/v31 v31.0.0 github.com/gorilla/mux v1.7.3 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 github.com/keybase/go-codec v0.0.0-20180928230036-164397562123 - github.com/keybase/go-keybase-chat-bot v0.0.0-20250106203511-859265729a56 + github.com/keybase/go-keybase-chat-bot v0.0.0-20251212163122-450fd0812017 github.com/opensearch-project/opensearch-go/v4 v4.5.0 github.com/stathat/go v1.0.0 - github.com/stretchr/testify v1.10.0 + github.com/stretchr/testify v1.11.1 github.com/xanzy/go-gitlab v0.29.0 - golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d - golang.org/x/sync v0.10.0 + golang.org/x/oauth2 v0.34.0 + golang.org/x/sync v0.19.0 google.golang.org/api v0.14.0 ) require ( filippo.io/edwards25519 v1.1.0 // indirect github.com/go-pkgz/expirable-cache v0.1.0 // indirect + github.com/golang-jwt/jwt/v4 v4.5.2 // indirect + github.com/google/go-github/v75 v75.0.0 // indirect ) require ( cloud.google.com/go v0.38.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect github.com/didip/tollbooth/v7 v7.0.1 github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 // indirect github.com/golang/protobuf v1.3.5 // indirect - github.com/google/go-github/v28 v28.1.1 // indirect - github.com/google/go-querystring v1.0.0 // indirect + github.com/google/go-querystring v1.1.0 // indirect github.com/googleapis/gax-go/v2 v2.0.5 // indirect github.com/hashicorp/go-cleanhttp v0.5.1 // indirect github.com/hashicorp/go-retryablehttp v0.6.4 // indirect @@ -44,10 +44,10 @@ require ( github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/pmezard/go-difflib v1.0.0 // indirect go.opencensus.io v0.22.1 // indirect - golang.org/x/crypto v0.1.0 // indirect - golang.org/x/net v0.7.0 // indirect - golang.org/x/sys v0.5.0 // indirect - golang.org/x/text v0.7.0 // indirect + golang.org/x/crypto v0.46.0 // indirect + golang.org/x/net v0.48.0 // indirect + golang.org/x/sys v0.39.0 // indirect + golang.org/x/text v0.32.0 // indirect golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect google.golang.org/appengine v1.6.5 // indirect google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873 // indirect diff --git a/go.sum b/go.sum index 85323299..bf7563cd 100644 --- a/go.sum +++ b/go.sum @@ -7,21 +7,21 @@ filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/aws/aws-sdk-go v1.55.7 h1:UJrkFq7es5CShfBwlWAC8DA077vp8PyVbQd3lqLiztE= github.com/aws/aws-sdk-go v1.55.7/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= -github.com/bradleyfalzon/ghinstallation v1.1.0 h1:mwazVinJU0mPyLxIcdtJzu4DhWXFO5lMsWhKyFRIwFk= -github.com/bradleyfalzon/ghinstallation v1.1.0/go.mod h1:p7iD8KytOOKg2wCqbwvJlq4JGpYMjwjkiqdyUqOIHLI= +github.com/bradleyfalzon/ghinstallation/v2 v2.17.0 h1:SmbUK/GxpAspRjSQbB6ARvH+ArzlNzTtHydNyXUQ6zg= +github.com/bradleyfalzon/ghinstallation/v2 v2.17.0/go.mod h1:vuD/xvJT9Y+ZVZRv4HQ42cMyPFIYqpc7AbB4Gvt/DlY= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/didip/tollbooth/v7 v7.0.1 h1:TkT4sBKoQoHQFPf7blQ54iHrZiTDnr8TceU+MulVAog= github.com/didip/tollbooth/v7 v7.0.1/go.mod h1:VZhDSGl5bDSPj4wPsih3PFa4Uh9Ghv8hgacaTm5PRT4= github.com/go-pkgz/expirable-cache v0.1.0 h1:3bw0m8vlTK8qlwz5KXuygNBTkiKRTPrAGXU0Ej2AC1g= github.com/go-pkgz/expirable-cache v0.1.0/go.mod h1:GTrEl0X+q0mPNqN6dtcQXksACnzCBQ5k/k1SwXJsZKs= -github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= -github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= +github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= +github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= +github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= +github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= @@ -36,14 +36,16 @@ github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgj github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-github/v28 v28.1.1 h1:kORf5ekX5qwXO2mGzXXOjMe/g6ap8ahVe0sBEulhSxo= -github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-github/v31 v31.0.0 h1:JJUxlP9lFK+ziXKimTCprajMApV1ecWD4NB6CCb0plo= github.com/google/go-github/v31 v31.0.0/go.mod h1:NQPZol8/1sMoWYGN2yaALIBytu17gAWfhbweiEed3pM= -github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= +github.com/google/go-github/v75 v75.0.0 h1:k7q8Bvg+W5KxRl9Tjq16a9XEgVY1pwuiG5sIL7435Ic= +github.com/google/go-github/v75 v75.0.0/go.mod h1:H3LUJEA1TCrzuUqtdAQniBNwuKiQIqdGKgBo1/M/uqI= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= @@ -68,8 +70,8 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNU github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/keybase/go-codec v0.0.0-20180928230036-164397562123 h1:yg56lYPqh9suJepqxOMd/liFgU/x+maRPiB30JNYykM= github.com/keybase/go-codec v0.0.0-20180928230036-164397562123/go.mod h1:r/eVVWCngg6TsFV/3HuS9sWhDkAzGG8mXhiuYA+Z/20= -github.com/keybase/go-keybase-chat-bot v0.0.0-20250106203511-859265729a56 h1:w8ikAizh5hbXZxBXbees5iOxOoi7nH/qp1lJQ3pOPiY= -github.com/keybase/go-keybase-chat-bot v0.0.0-20250106203511-859265729a56/go.mod h1:cmXzSxB8TNJdxMKcmywTHsbv+H3WZ/92lP9nyEbCGNQ= +github.com/keybase/go-keybase-chat-bot v0.0.0-20251212163122-450fd0812017 h1:lB6jgDag58Ie9yfLSGDQiUZt60zPyRpK6aWCtovQeSo= +github.com/keybase/go-keybase-chat-bot v0.0.0-20251212163122-450fd0812017/go.mod h1:wl5lBoVNkepL8Hzs7jyqg3GS6U+by4yQeNr7oT0Evt0= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -86,8 +88,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= @@ -104,8 +106,8 @@ go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.1 h1:8dP3SGL7MPB94crU3bEPplMPe83FI4EouesJUeFHv50= go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= -golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= +golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -121,32 +123,32 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= +golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= +golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -156,6 +158,7 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.14.0 h1:uMf5uLi4eQMRrMKhCplNik4U4H8Z6C1br3zOtAa/aDE= google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= diff --git a/macrobot/macrobot/db.go b/macrobot/macrobot/db.go index 02883f39..eaab01c5 100644 --- a/macrobot/macrobot/db.go +++ b/macrobot/macrobot/db.go @@ -2,6 +2,7 @@ package macrobot import ( "database/sql" + "fmt" "github.com/keybase/go-keybase-chat-bot/kbchat/types/chat1" "github.com/keybase/managed-bots/base" @@ -45,7 +46,7 @@ func (d *DB) Create(name string, convID chat1.ConvIDStr, isConv bool, macroName, } func (d *DB) Get(name string, convID chat1.ConvIDStr, macroName string) (message string, err error) { - row := d.DB.QueryRow(` + row := d.QueryRow(` SELECT macro_message FROM macro WHERE (channel_name = ? OR channel_name = ?) AND macro_name = ? @@ -64,7 +65,7 @@ type Macro struct { } func (d *DB) List(name string, convID chat1.ConvIDStr) (list []Macro, err error) { - rows, err := d.DB.Query(` + rows, err := d.Query(` SELECT macro_name, macro_message, is_conv FROM macro WHERE channel_name = ? @@ -75,7 +76,11 @@ func (d *DB) List(name string, convID chat1.ConvIDStr) (list []Macro, err error) if err != nil { return nil, err } - defer rows.Close() + defer func() { + if cerr := rows.Close(); cerr != nil { + fmt.Printf("List: failed to close rows: %v\n", cerr) + } + }() for rows.Next() { var macro Macro if err := rows.Scan(¯o.Name, ¯o.Message, ¯o.IsConv); err != nil { diff --git a/macrobot/main.go b/macrobot/main.go index ec3478f1..fe379ac3 100644 --- a/macrobot/main.go +++ b/macrobot/main.go @@ -93,7 +93,11 @@ func (s *BotServer) Go() (err error) { s.Errorf("failed to connect to MySQL: %s", err) return err } - defer sdb.Close() + defer func() { + if cerr := sdb.Close(); cerr != nil { + fmt.Printf("failed to close DB: %v\n", cerr) + } + }() db := macrobot.NewDB(sdb) debugConfig := base.NewChatDebugOutputConfig(s.kbc, s.opts.ErrReportConv) diff --git a/meetbot/main.go b/meetbot/main.go index 4ed87198..7ddfedf4 100644 --- a/meetbot/main.go +++ b/meetbot/main.go @@ -104,7 +104,11 @@ func (s *BotServer) Go() (err error) { s.Errorf("failed to connect to MySQL: %s", err) return err } - defer sdb.Close() + defer func() { + if cerr := sdb.Close(); cerr != nil { + fmt.Printf("failed to close DB: %v\n", cerr) + } + }() db := base.NewOAuthDB(sdb) debugConfig := base.NewChatDebugOutputConfig(s.kbc, s.opts.ErrReportConv) stats, err := base.NewStatsRegistry(debugConfig, s.opts.StathatEZKey) diff --git a/meetbot/meetbot/handler.go b/meetbot/meetbot/handler.go index 8f7a35fb..e81dc656 100644 --- a/meetbot/meetbot/handler.go +++ b/meetbot/meetbot/handler.go @@ -25,8 +25,13 @@ type Handler struct { var _ base.Handler = (*Handler)(nil) -func NewHandler(stats *base.StatsRegistry, kbc *kbchat.API, debugConfig *base.ChatDebugOutputConfig, - db *base.OAuthDB, config *oauth2.Config) *Handler { +func NewHandler( + stats *base.StatsRegistry, + kbc *kbchat.API, + debugConfig *base.ChatDebugOutputConfig, + db *base.OAuthDB, + config *oauth2.Config, +) *Handler { return &Handler{ DebugOutput: base.NewDebugOutput("Handler", debugConfig), stats: stats.SetPrefix("Handler"), diff --git a/meetbot/meetbot/http.go b/meetbot/meetbot/http.go index 161c6e39..57829ee5 100644 --- a/meetbot/meetbot/http.go +++ b/meetbot/meetbot/http.go @@ -21,7 +21,10 @@ type HTTPSrv struct { } func NewHTTPSrv(stats *base.StatsRegistry, kbc *kbchat.API, debugConfig *base.ChatDebugOutputConfig, - db *base.OAuthDB, handler *Handler, oauthConfig *oauth2.Config) *HTTPSrv { + db *base.OAuthDB, + handler *Handler, + oauthConfig *oauth2.Config, +) *HTTPSrv { h := &HTTPSrv{ db: db, handler: handler, @@ -35,7 +38,9 @@ func NewHTTPSrv(stats *base.StatsRegistry, kbc *kbchat.API, debugConfig *base.Ch } func (h *HTTPSrv) healthCheckHandler(w http.ResponseWriter, _ *http.Request) { - fmt.Fprintf(w, "OK") + if _, err := fmt.Fprintf(w, "OK"); err != nil { + h.Debug("healthCheckHandler: failed to write response: %s", err) + } } func (h *HTTPSrv) homeHandler(w http.ResponseWriter, _ *http.Request) { diff --git a/pollbot/main.go b/pollbot/main.go index 567b0d48..e42a49d9 100644 --- a/pollbot/main.go +++ b/pollbot/main.go @@ -103,7 +103,11 @@ func (s *BotServer) Go() (err error) { s.Errorf("failed to connect to MySQL: %s", err) return err } - defer sdb.Close() + defer func() { + if cerr := sdb.Close(); cerr != nil { + fmt.Printf("failed to close DB: %v\n", cerr) + } + }() db := pollbot.NewDB(sdb) debugConfig := base.NewChatDebugOutputConfig(s.kbc, s.opts.ErrReportConv) diff --git a/pollbot/pollbot/db.go b/pollbot/pollbot/db.go index 2bf171bf..2c437e0a 100644 --- a/pollbot/pollbot/db.go +++ b/pollbot/pollbot/db.go @@ -2,6 +2,7 @@ package pollbot import ( "database/sql" + "fmt" "github.com/keybase/go-keybase-chat-bot/kbchat/types/chat1" "github.com/keybase/managed-bots/base" @@ -37,7 +38,7 @@ func (d *DB) CreatePoll(id string, convID chat1.ConvIDStr, msgID chat1.MessageID } func (d *DB) GetPollInfo(id string) (convID chat1.ConvIDStr, resultMsgID chat1.MessageID, numChoices int, err error) { - row := d.DB.QueryRow(` + row := d.QueryRow(` SELECT conv_id, result_msg_id, choices FROM polls WHERE id = ? @@ -49,7 +50,7 @@ func (d *DB) GetPollInfo(id string) (convID chat1.ConvIDStr, resultMsgID chat1.M } func (d *DB) GetTally(id string) (res Tally, err error) { - rows, err := d.DB.Query(` + rows, err := d.Query(` SELECT choice, count(*) FROM votes WHERE id = ? @@ -58,7 +59,11 @@ func (d *DB) GetTally(id string) (res Tally, err error) { if err != nil { return res, err } - defer rows.Close() + defer func() { + if cerr := rows.Close(); cerr != nil { + fmt.Printf("GetTally: failed to close rows: %v\n", cerr) + } + }() for rows.Next() { var tres TallyResult if err := rows.Scan(&tres.choice, &tres.votes); err != nil { diff --git a/pollbot/pollbot/handler.go b/pollbot/pollbot/handler.go index 041cb7cd..ef3e3d99 100644 --- a/pollbot/pollbot/handler.go +++ b/pollbot/pollbot/handler.go @@ -23,8 +23,14 @@ type Handler struct { var _ base.Handler = (*Handler)(nil) -func NewHandler(stats *base.StatsRegistry, kbc *kbchat.API, debugConfig *base.ChatDebugOutputConfig, - httpSrv *HTTPSrv, db *DB, httpPrefix string) *Handler { +func NewHandler( + stats *base.StatsRegistry, + kbc *kbchat.API, + debugConfig *base.ChatDebugOutputConfig, + httpSrv *HTTPSrv, + db *DB, + httpPrefix string, +) *Handler { return &Handler{ DebugOutput: base.NewDebugOutput("Handler", debugConfig), stats: stats.SetPrefix("Handler"), @@ -41,8 +47,7 @@ func (h *Handler) generateVoteLink(id string, choice int) string { return strings.ReplaceAll(link, "%", "%%") } -func (h *Handler) generateAnonymousPoll(convID chat1.ConvIDStr, prompt string, - options []string) error { +func (h *Handler) generateAnonymousPoll(convID chat1.ConvIDStr, prompt string, options []string) error { id := base.RandHexString(8) promptBody := fmt.Sprintf("Anonymous Poll: *%s*\n\n", prompt) sendRes, err := h.kbc.SendMessageByConvID(convID, "%s", promptBody) @@ -72,8 +77,7 @@ func (h *Handler) generateAnonymousPoll(convID chat1.ConvIDStr, prompt string, return nil } -func (h *Handler) generatePoll(convID chat1.ConvIDStr, prompt string, - options []string) error { +func (h *Handler) generatePoll(convID chat1.ConvIDStr, prompt string, options []string) error { body := fmt.Sprintf("Poll: *%s*\n\n", prompt) for index, option := range options { body += fmt.Sprintf("%s %s\n", base.NumberToEmoji(index+1), option) @@ -130,8 +134,8 @@ func (h *Handler) handlePoll(cmd string, convID chat1.ConvIDStr) error { func (h *Handler) handleLogin(convName, username string) { // make sure we are in a conv with just the person - if !(convName == fmt.Sprintf("%s,%s", username, h.kbc.GetUsername()) || - convName == fmt.Sprintf("%s,%s", h.kbc.GetUsername(), username)) { + if convName != fmt.Sprintf("%s,%s", username, h.kbc.GetUsername()) && + convName != fmt.Sprintf("%s,%s", h.kbc.GetUsername(), username) { return } token := h.httpSrv.LoginToken(username) diff --git a/pollbot/pollbot/http.go b/pollbot/pollbot/http.go index ff814a8b..c3a16679 100644 --- a/pollbot/pollbot/http.go +++ b/pollbot/pollbot/http.go @@ -26,7 +26,9 @@ type HTTPSrv struct { } func NewHTTPSrv(stats *base.StatsRegistry, kbc *kbchat.API, debugConfig *base.ChatDebugOutputConfig, - db *DB, tokenSecret string) *HTTPSrv { + db *DB, + tokenSecret string, +) *HTTPSrv { h := &HTTPSrv{ kbc: kbc, db: db, diff --git a/triviabot/main.go b/triviabot/main.go index e8d6dd15..a4363103 100644 --- a/triviabot/main.go +++ b/triviabot/main.go @@ -72,7 +72,11 @@ func (s *BotServer) Go() (err error) { s.Errorf("failed to connect to MySQL: %s", err) return err } - defer sdb.Close() + defer func() { + if cerr := sdb.Close(); cerr != nil { + fmt.Printf("failed to close DB: %v\n", cerr) + } + }() db := triviabot.NewDB(sdb) debugConfig := base.NewChatDebugOutputConfig(s.kbc, s.opts.ErrReportConv) diff --git a/triviabot/triviabot/db.go b/triviabot/triviabot/db.go index b9d3ae5c..b23789d5 100644 --- a/triviabot/triviabot/db.go +++ b/triviabot/triviabot/db.go @@ -2,6 +2,7 @@ package triviabot import ( "database/sql" + "fmt" "github.com/keybase/go-keybase-chat-bot/kbchat/types/chat1" "github.com/keybase/managed-bots/base" @@ -56,7 +57,11 @@ func (d *DB) TopUsers(convID chat1.ConvIDStr) (res []TopUser, err error) { if err != nil { return res, err } - defer rows.Close() + defer func() { + if cerr := rows.Close(); cerr != nil { + fmt.Printf("TopUsers: failed to close rows: %v\n", cerr) + } + }() for rows.Next() { var user TopUser if err := rows.Scan(&user.Username, &user.Points, &user.Correct, &user.Incorrect); err != nil { diff --git a/triviabot/triviabot/handler.go b/triviabot/triviabot/handler.go index 6a4c5a72..422f2f78 100644 --- a/triviabot/triviabot/handler.go +++ b/triviabot/triviabot/handler.go @@ -39,10 +39,7 @@ func (h *Handler) handleStart(msg chat1.MsgSummary) { defer h.Unlock() convID := msg.ConvID session := newSession(h.kbc, h.debugConfig, h.db, convID) - doneCb, err := session.start(0) - if err != nil { - h.ChatErrorf(convID, "handleState: failed to start: %s", err) - } + doneCb := session.start(0) h.sessions[convID] = session base.GoWithRecover(h.DebugOutput, func() { <-doneCb diff --git a/triviabot/triviabot/session.go b/triviabot/triviabot/session.go index 55235ee7..626a45e0 100644 --- a/triviabot/triviabot/session.go +++ b/triviabot/triviabot/session.go @@ -117,6 +117,7 @@ func newSession(kbc *kbchat.API, debugConfig *base.ChatDebugOutputConfig, db *DB } func (s *session) getCategory() int { + //nolint:gosec // math/rand is sufficient for non-security-sensitive trivia category selection return eligibleCategories[rand.Intn(len(eligibleCategories))] } @@ -125,7 +126,9 @@ func (s *session) getAPIToken() (string, error) { if err != nil { return "", err } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() var apiResp apiTokenResponse decoder := json.NewDecoder(resp.Body) if err := decoder.Decode(&apiResp); err != nil { @@ -173,11 +176,14 @@ func (s *session) getNextQuestion() error { url := fmt.Sprintf("https://opentdb.com/api.php?amount=1&category=%d&token=%s&type=multiple", s.getCategory(), token) s.Debug("getNextQuestion: url: %s", url) + //nolint:gosec // URL is constructed from trusted API base and validated token resp, err := http.Get(url) if err != nil { return err } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() decoder := json.NewDecoder(resp.Body) if err := decoder.Decode(&apiResp); err != nil { return err @@ -349,8 +355,8 @@ func (s *session) waitForCorrectAnswer() { } } -func (s *session) start(intotal int) (doneCb chan struct{}, err error) { - doneCb = make(chan struct{}) +func (s *session) start(intotal int) chan struct{} { + doneCb := make(chan struct{}) total := defaultTotal if intotal > 0 { total = intotal @@ -380,7 +386,7 @@ func (s *session) start(intotal int) (doneCb chan struct{}, err error) { s.waitForCorrectAnswer() } }) - return doneCb, nil + return doneCb } func (s *session) stop() { diff --git a/webhookbot/main.go b/webhookbot/main.go index 2f4d05e9..8af9d106 100644 --- a/webhookbot/main.go +++ b/webhookbot/main.go @@ -44,8 +44,10 @@ func NewBotServer(opts Options) *BotServer { } } -const back = "`" -const backs = "```" +const ( + back = "`" + backs = "```" +) func (s *BotServer) makeAdvertisement() kbchat.Advertisement { createExtended := fmt.Sprintf(`Create a new webhook for sending messages into the current conversation. You must supply a name as well to identify the webhook. To use a webhook URL, supply a %smsg%s URL parameter, or a JSON POST body with a field %smsg%s. @@ -104,7 +106,11 @@ func (s *BotServer) Go() (err error) { s.Errorf("failed to connect to MySQL: %s", err) return err } - defer sdb.Close() + defer func() { + if cerr := sdb.Close(); cerr != nil { + fmt.Printf("failed to close DB: %v\n", cerr) + } + }() db := webhookbot.NewDB(sdb) debugConfig := base.NewChatDebugOutputConfig(s.kbc, s.opts.ErrReportConv) diff --git a/webhookbot/webhookbot/db.go b/webhookbot/webhookbot/db.go index 0db7a04c..f9fd2c1f 100644 --- a/webhookbot/webhookbot/db.go +++ b/webhookbot/webhookbot/db.go @@ -5,6 +5,7 @@ import ( "crypto/sha256" "database/sql" "encoding/hex" + "fmt" "github.com/keybase/go-keybase-chat-bot/kbchat/types/chat1" "github.com/keybase/managed-bots/base" @@ -55,7 +56,7 @@ func (d *DB) Create(name string, convID chat1.ConvIDStr) (string, error) { } func (d *DB) GetHook(id string) (res Webhook, err error) { - row := d.DB.QueryRow(` + row := d.QueryRow(` SELECT conv_id, name FROM hooks WHERE id = ? `, id) if err := row.Scan(&res.ConvID, &res.Name); err != nil { @@ -71,13 +72,17 @@ type Webhook struct { } func (d *DB) List(convID chat1.ConvIDStr) (res []Webhook, err error) { - rows, err := d.DB.Query(` + rows, err := d.Query(` SELECT id, name FROM hooks WHERE conv_id = ? `, convID) if err != nil { return nil, err } - defer rows.Close() + defer func() { + if cerr := rows.Close(); cerr != nil { + fmt.Printf("List: failed to close rows: %v\n", cerr) + } + }() for rows.Next() { var hook Webhook hook.ConvID = convID diff --git a/webhookbot/webhookbot/handler.go b/webhookbot/webhookbot/handler.go index b2f4a1ad..8e6cbb41 100644 --- a/webhookbot/webhookbot/handler.go +++ b/webhookbot/webhookbot/handler.go @@ -22,8 +22,7 @@ type Handler struct { var _ base.Handler = (*Handler)(nil) -func NewHandler(stats *base.StatsRegistry, kbc *kbchat.API, debugConfig *base.ChatDebugOutputConfig, - httpSrv *HTTPSrv, db *DB, httpPrefix string) *Handler { +func NewHandler(stats *base.StatsRegistry, kbc *kbchat.API, debugConfig *base.ChatDebugOutputConfig, httpSrv *HTTPSrv, db *DB, httpPrefix string) *Handler { return &Handler{ DebugOutput: base.NewDebugOutput("Handler", debugConfig), stats: stats.SetPrefix("Handler"), diff --git a/webhookbot/webhookbot/http.go b/webhookbot/webhookbot/http.go index 41c30412..6ebb301f 100644 --- a/webhookbot/webhookbot/http.go +++ b/webhookbot/webhookbot/http.go @@ -44,7 +44,11 @@ func (h *HTTPSrv) getMessage(r *http.Request) (string, error) { return msg, nil } - defer r.Body.Close() + defer func() { + if cerr := r.Body.Close(); cerr != nil { + h.Errorf("getMessage: failed to close request body: %s", cerr) + } + }() body, err := io.ReadAll(r.Body) if err != nil { return "", err @@ -69,7 +73,11 @@ func (h *HTTPSrv) safeWriteToFile(hookName, content string) (string, error) { if err != nil { return "", fmt.Errorf("failed to create temp file: %w", err) } - defer file.Close() + defer func() { + if cerr := file.Close(); cerr != nil { + h.Errorf("safeWriteToFile: failed to close temp file: %s", cerr) + } + }() if _, err := file.Write([]byte(content)); err != nil { return "", fmt.Errorf("failed to write file: %w", err) diff --git a/zoombot/main.go b/zoombot/main.go index 50beaf33..25c60217 100644 --- a/zoombot/main.go +++ b/zoombot/main.go @@ -134,7 +134,11 @@ func (s *BotServer) Go() (err error) { s.Errorf("failed to connect to MySQL: %s", err) return err } - defer sdb.Close() + defer func() { + if cerr := sdb.Close(); cerr != nil { + fmt.Printf("failed to close DB: %v\n", cerr) + } + }() db := zoombot.NewDB(sdb) debugConfig := base.NewChatDebugOutputConfig(s.kbc, s.opts.ErrReportConv) diff --git a/zoombot/zoombot/api.go b/zoombot/zoombot/api.go index 94209399..ff2d4a86 100644 --- a/zoombot/zoombot/api.go +++ b/zoombot/zoombot/api.go @@ -158,7 +158,9 @@ func GetUser(client *http.Client, userID string) (*GetUserResponse, error) { return nil, err } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() data, err := io.ReadAll(resp.Body) if err != nil { @@ -190,7 +192,9 @@ func CreateMeeting(client *http.Client, userID string, request *CreateMeetingReq return nil, err } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() data, err := io.ReadAll(resp.Body) if err != nil { diff --git a/zoombot/zoombot/handler.go b/zoombot/zoombot/handler.go index e22563bd..e4ae4609 100644 --- a/zoombot/zoombot/handler.go +++ b/zoombot/zoombot/handler.go @@ -24,8 +24,7 @@ type Handler struct { var _ base.Handler = (*Handler)(nil) -func NewHandler(stats *base.StatsRegistry, kbc *kbchat.API, debugConfig *base.ChatDebugOutputConfig, - db *DB, config *oauth2.Config) *Handler { +func NewHandler(stats *base.StatsRegistry, kbc *kbchat.API, debugConfig *base.ChatDebugOutputConfig, db *DB, config *oauth2.Config) *Handler { return &Handler{ DebugOutput: base.NewDebugOutput("Handler", debugConfig), stats: stats.SetPrefix("Handler"), diff --git a/zoombot/zoombot/http.go b/zoombot/zoombot/http.go index e995755f..f55c72b7 100644 --- a/zoombot/zoombot/http.go +++ b/zoombot/zoombot/http.go @@ -25,8 +25,15 @@ type HTTPSrv struct { credentials *Credentials } -func NewHTTPSrv(stats *base.StatsRegistry, kbc *kbchat.API, debugConfig *base.ChatDebugOutputConfig, - db *DB, handler *Handler, oauthConfig *oauth2.Config, credentials *Credentials) *HTTPSrv { +func NewHTTPSrv( + stats *base.StatsRegistry, + kbc *kbchat.API, + debugConfig *base.ChatDebugOutputConfig, + db *DB, + handler *Handler, + oauthConfig *oauth2.Config, + credentials *Credentials, +) *HTTPSrv { h := &HTTPSrv{ db: db, handler: handler, @@ -41,11 +48,15 @@ func NewHTTPSrv(stats *base.StatsRegistry, kbc *kbchat.API, debugConfig *base.Ch } func (h *HTTPSrv) healthCheckHandler(w http.ResponseWriter, _ *http.Request) { - fmt.Fprintf(w, "OK") + if _, err := fmt.Fprintf(w, "OK"); err != nil { + h.Debug("healthCheckHandler: failed to write response: %s", err) + } } func (h *HTTPSrv) supportHandler(w http.ResponseWriter, _ *http.Request) { - fmt.Fprint(w, supportHTML) + if _, err := fmt.Fprint(w, supportHTML); err != nil { + h.Debug("supportHandler: failed to write response: %s", err) + } } // see https://developers.zoom.us/docs/api/webhooks/#verify-with-zooms-header @@ -67,7 +78,11 @@ func (h *HTTPSrv) validateWebhookMessage(bodyBytes []byte, r *http.Request) (err func (h *HTTPSrv) zoomDeauthorize(w http.ResponseWriter, r *http.Request) { bodyBytes, err := io.ReadAll(r.Body) - defer r.Body.Close() + defer func() { + if cerr := r.Body.Close(); cerr != nil { + h.Errorf("zoomDeauthorize: unable to close body: %s", cerr) + } + }() if err != nil { h.Errorf("zoomDeauthorize: unable to read body: %s", err) http.Error(w, err.Error(), http.StatusBadRequest)