Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions internal/conf/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"encoding/json"
"errors"
"fmt"
"math"
"net/url"
"regexp"
"strings"
Expand All @@ -25,6 +26,12 @@ const defaultChallengeExpiryDuration float64 = 300
const defaultFactorExpiryDuration time.Duration = 300 * time.Second
const defaultFlowStateExpiryDuration time.Duration = 300 * time.Second

// maxOtpExp is the largest OTP validity window, in seconds, that survives
// conversion to a time.Duration, which counts nanoseconds in an int64. That is
// roughly 292 years. ApplyDefaults clamps OtpExp to it so the conversion cannot
// overflow into a negative duration, which would expire tokens at creation.
const maxOtpExp uint = math.MaxInt64 / uint(time.Second)
Comment thread
xlgmokha marked this conversation as resolved.

// See: https://www.postgresql.org/docs/7.0/syntax525.htm
var postgresNamesRegexp = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_]{0,62}$`)

Expand Down Expand Up @@ -1203,6 +1210,10 @@ func (config *GlobalConfiguration) ApplyDefaults() error {
config.Mailer.OtpExp = 86400 // 1 day
}

if config.Mailer.OtpExp > maxOtpExp {
config.Mailer.OtpExp = maxOtpExp
Comment thread
xlgmokha marked this conversation as resolved.
}

if config.Mailer.OtpLength == 0 || config.Mailer.OtpLength < 6 || config.Mailer.OtpLength > 10 {
// 6-digit otp by default
config.Mailer.OtpLength = 6
Expand All @@ -1220,6 +1231,10 @@ func (config *GlobalConfiguration) ApplyDefaults() error {
config.Sms.OtpExp = 60
}

if config.Sms.OtpExp > maxOtpExp {
config.Sms.OtpExp = maxOtpExp
}

if config.Sms.OtpLength == 0 || config.Sms.OtpLength < 6 || config.Sms.OtpLength > 10 {
// 6-digit otp by default
config.Sms.OtpLength = 6
Expand Down
63 changes: 63 additions & 0 deletions internal/conf/configuration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package conf
import (
"encoding/base64"
"errors"
"math"
"os"
"sort"
"strings"
Expand Down Expand Up @@ -1251,3 +1252,65 @@ func TestProviderLinkingDomainsDecode(t *testing.T) {
require.Error(t, d.Decode("custom:github="))
}
}

func TestApplyDefaultsClampsOtpExp(t *testing.T) {
baseConfig := func() *GlobalConfiguration {
c := &GlobalConfiguration{}
c.JWT.Secret = "secret"
return c
}

cases := []struct {
desc string
mailerExp uint
smsExp uint
wantMailer uint
wantSms uint
}{
{
desc: "zero takes the per-channel default",
Comment thread
annabkr marked this conversation as resolved.
mailerExp: 0,
smsExp: 0,
wantMailer: 86400,
wantSms: 60,
},
{
desc: "in-range values pass through untouched",
mailerExp: 3600,
smsExp: 120,
wantMailer: 3600,
wantSms: 120,
},
{
desc: "the ceiling itself is not clamped",
mailerExp: maxOtpExp,
smsExp: maxOtpExp,
wantMailer: maxOtpExp,
wantSms: maxOtpExp,
},
{
desc: "values above the ceiling clamp down to it",
mailerExp: maxOtpExp + 1,
smsExp: math.MaxUint,
wantMailer: maxOtpExp,
wantSms: maxOtpExp,
},
}

for _, c := range cases {
t.Run(c.desc, func(t *testing.T) {
cfg := baseConfig()
cfg.Mailer.OtpExp = c.mailerExp
cfg.Sms.OtpExp = c.smsExp
require.NoError(t, cfg.ApplyDefaults())

require.Equal(t, c.wantMailer, cfg.Mailer.OtpExp)
require.Equal(t, c.wantSms, cfg.Sms.OtpExp)

// the clamp exists so that this conversion cannot overflow into a
// negative duration, which would expire every token at creation
require.Positive(t, time.Duration(cfg.Mailer.OtpExp)*time.Second) // #nosec G115
require.Positive(t, time.Duration(cfg.Sms.OtpExp)*time.Second) // #nosec G115
})
}
}
Loading