Skip to content

Commit a585abe

Browse files
authored
Merge pull request #176 from ritsec/force-wait-before-email-bypass
Force wait before email bypass
2 parents d5dde09 + 730c753 commit a585abe

1 file changed

Lines changed: 46 additions & 1 deletion

File tree

commands/slash/member.go

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"fmt"
55
"regexp"
66
"strings"
7+
"sync"
8+
"time"
79

810
"github.com/bwmarrin/discordgo"
911
"github.com/google/uuid"
@@ -39,7 +41,16 @@ var (
3941
alumniRole string = config.GetString("commands.member.alumni_role_id")
4042
)
4143

42-
// Member is the member command
44+
// notReceivedCooldown tracks the last time a verification email was sent in
45+
// the /member flow, per user ID. Once the email goes out, a user must wait
46+
// ten minutes before declaring they did not recieve the code; the "I recieved
47+
// the code" path is never gated.
48+
var notReceivedCooldown = struct {
49+
sync.Mutex
50+
m map[string]time.Time // user ID -> time the verification email was sent
51+
}{m: make(map[string]time.Time)}
52+
53+
// Member is the handler for the /member command
4354
func Member() (*discordgo.ApplicationCommand, func(s *discordgo.Session, i *discordgo.InteractionCreate)) {
4455
return &discordgo.ApplicationCommand{
4556
Name: "member",
@@ -473,6 +484,13 @@ func recievedEmail(s *discordgo.Session, i *discordgo.InteractionCreate, userEma
473484
)
474485
defer span.Finish()
475486

487+
// Anchor the cooldown now, at the moment the code email was sent: from
488+
// here the user must wait ten minutes before declaring the code never
489+
// arrived, and every new email send restarts that window.
490+
notReceivedCooldown.Lock()
491+
notReceivedCooldown.m[i.Member.User.ID] = time.Now()
492+
notReceivedCooldown.Unlock()
493+
476494
interactionCreateChan := make(chan *discordgo.InteractionCreate)
477495
defer close(interactionCreateChan)
478496

@@ -489,6 +507,33 @@ func recievedEmail(s *discordgo.Session, i *discordgo.InteractionCreate, userEma
489507
defer delete(*ComponentHandlers, recievedSlug)
490508

491509
(*ComponentHandlers)[unrecievedSlug] = func(s *discordgo.Session, i *discordgo.InteractionCreate) {
510+
// The cooldown was anchored when the verification email was sent; refuse
511+
// until the ten-minute delivery window has passed.
512+
notReceivedCooldown.Lock()
513+
last, ok := notReceivedCooldown.m[i.Member.User.ID]
514+
blocked := ok && time.Now().Before(last.Add(10*time.Minute))
515+
notReceivedCooldown.Unlock()
516+
if blocked {
517+
remaining := int(time.Until(last.Add(10*time.Minute)).Seconds())/60 + 1
518+
units := "minutes"
519+
if remaining == 1 {
520+
units = "minute"
521+
}
522+
err := s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
523+
Type: discordgo.InteractionResponseChannelMessageWithSource,
524+
Data: &discordgo.InteractionResponseData{
525+
Flags: discordgo.MessageFlagsEphemeral,
526+
Content: fmt.Sprintf("You can only request manual verification after 10 minutes. Please wait **%d** %s and try again.", remaining, units),
527+
},
528+
})
529+
if err != nil {
530+
logging.Error(s, err.Error(), i.Member.User, span, logrus.Fields{"error": err})
531+
return
532+
}
533+
// Do not push to the channels; the prompt above stays open and
534+
// the function continues waiting for the user to click a button.
535+
return
536+
}
492537
recievedChan <- false
493538
interactionCreateChan <- i
494539
}

0 commit comments

Comments
 (0)