Skip to content

Commit

Permalink
add optional dkim configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
qhenkart committed Mar 15, 2024
1 parent 574e54c commit 3d335f3
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 6 deletions.
33 changes: 30 additions & 3 deletions helpers/inbound/inbound.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,10 +233,37 @@ func (email *ParsedEmail) parseHeaders(headers string) {
}
}

// ValidateConfig adds configuration options for validating a parsed email. Specifically turning DKIM as optional. DKIM validates emails in transit from tampering. However many valid email clients
// do not have it configured
// SPF could potentially be added but it poses a more significant security risk
type ValidateConfig struct {
dkimOptional bool
}

// NewValidateConfig returns a configuration struct for validating the parsed email. Specifically turning DKIM as optional. DKIM validates emails in transit from tampering. However many valid email clients
// do not have it configured
func NewValidateConfig() ValidateConfig {
return ValidateConfig{}
}

// WithDKIMOptional validates DKIM values only if they exist. This allows the inbound client to support more email clients that might not support this configuration
func (vc ValidateConfig) WithDKIMOptional() ValidateConfig {
vc.dkimOptional = true
return vc
}

// Validate validates the DKIM and SPF scores to ensure that the email client and address was not spoofed
func (email *ParsedEmail) Validate() error {
if len(email.rawValues["dkim"]) == 0 || len(email.rawValues["SPF"]) == 0 {
return fmt.Errorf("missing DKIM and SPF score")
func (email *ParsedEmail) Validate(config ...ValidateConfig) error {
var dkimOptional bool
if len(config) > 0 {
dkimOptional = config[0].dkimOptional
}
if len(email.rawValues["SPF"]) == 0 {
return fmt.Errorf("missing SPF score")
}

if !dkimOptional && len(email.rawValues["dkim"]) == 0 {
return fmt.Errorf("missing DKIM score")
}

for _, val := range email.rawValues["dkim"] {
Expand Down
27 changes: 24 additions & 3 deletions helpers/inbound/inbound_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,13 +136,18 @@ func TestValidate(t *testing.T) {
name string
values map[string][]string
expectedError error
config ValidateConfig
}{
{
name: "MissingHeaders",
name: "MissingSPFHeaders",
values: map[string][]string{},
expectedError: fmt.Errorf("missing DKIM and SPF score"),
expectedError: fmt.Errorf("missing SPF score"),
},
{
name: "MissingDKIMHeaders",
values: map[string][]string{"SPF": {"pass"}},
expectedError: fmt.Errorf("missing DKIM score"),
}, {
name: "FailedDkim",
values: map[string][]string{"dkim": {"pass", "fail", "pass"}, "SPF": {"pass"}},
expectedError: fmt.Errorf("DKIM validation failed"),
Expand All @@ -161,13 +166,29 @@ func TestValidate(t *testing.T) {
name: "success",
values: map[string][]string{"dkim": {"pass", "pass", "pass"}, "SPF": {"pass", "pass", "pass"}},
},
{
name: "dkimOptionalAndPresent",
values: map[string][]string{"dkim": {"pass", "pass", "pass"}, "SPF": {"pass", "pass", "pass"}},
config: NewValidateConfig().WithDKIMOptional(),
},
{
name: "dkimOptionalWithFailure",
values: map[string][]string{"dkim": {"fail", "pass", "pass"}, "SPF": {"pass", "pass", "pass"}},
config: NewValidateConfig().WithDKIMOptional(),
expectedError: fmt.Errorf("DKIM validation failed"),
},
{
name: "dkimOptionalAndAbsent",
values: map[string][]string{"SPF": {"pass", "pass", "pass"}},
config: NewValidateConfig().WithDKIMOptional(),
},
}

for _, test := range tests {
t.Run(test.name, func(subTest *testing.T) {
//Load POST body
email := ParsedEmail{rawValues: test.values}
err := email.Validate()
err := email.Validate(test.config)

if test.expectedError != nil {
assert.EqualError(subTest, test.expectedError, err.Error())
Expand Down

0 comments on commit 3d335f3

Please sign in to comment.