• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

LouisBrunner / terraform-aws-ses-forwarder / 6674035646

28 Oct 2023 01:46AM UTC coverage: 85.714% (-2.0%) from 87.671%
6674035646

push

github

LouisBrunner
fix: test + forgot file

126 of 147 relevant lines covered (85.71%)

1.03 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.0
/pkg/mailer/mailer.go
1
package mailer
2

3
import (
4
        "bytes"
5
        "encoding/json"
6
        "errors"
7
        "regexp"
8

9
        "github.com/aws/aws-lambda-go/events"
10
        "github.com/aws/aws-sdk-go/aws/client"
11
        "github.com/aws/aws-sdk-go/service/ses"
12
)
13

14
const passVerdict = "PASS"
15

16
type raw struct {
17
        events.SimpleEmailService
18
        Content string `json:"content"`
19
}
20

21
// Event contains a SES event
22
type Event struct {
23
        From  string
24
        To    string
25
        email []byte
26
}
27

28
func lookupHeader(headers []events.SimpleEmailHeader, name string) (string, bool) {
1✔
29
        for _, header := range headers {
2✔
30
                if header.Name == name {
2✔
31
                        return header.Value, true
1✔
32
                }
1✔
33
        }
34
        return "", false
1✔
35
}
36

37
// ParseEvent will try to transform the argument in a SES event
38
func ParseEvent(rawJSON []byte) (*Event, error) {
1✔
39
        var rawEvent raw
1✔
40
        err := json.Unmarshal(rawJSON, &rawEvent)
1✔
41
        if err != nil {
2✔
42
                return nil, err
1✔
43
        }
1✔
44

45
        if len(rawEvent.Content) < 1 {
2✔
46
                return nil, errors.New("missing `content` in SES event")
1✔
47
        }
1✔
48
        to, has := lookupHeader(rawEvent.Mail.Headers, "To")
1✔
49
        if !has || to == "" {
2✔
50
                return nil, errors.New("missing `mail.headers.to` in SES event")
1✔
51
        }
1✔
52
        if rawEvent.Receipt.SpamVerdict.Status != passVerdict || rawEvent.Receipt.VirusVerdict.Status != passVerdict {
2✔
53
                return nil, errors.New("don't forward spam/virus")
1✔
54
        }
1✔
55
        from, _ := lookupHeader(rawEvent.Mail.Headers, "From")
1✔
56

1✔
57
        event := Event{
1✔
58
                From:  from,
1✔
59
                To:    to,
1✔
60
                email: []byte(rawEvent.Content),
1✔
61
        }
1✔
62
        return &event, nil
1✔
63
}
64

65
var headerFromExp = regexp.MustCompile("(^|\n)From: ([^\r\n]*)(\r?\n)")
66
var headerToExp = regexp.MustCompile("(^|\n)To: [^\r\n]*(\r?\n)")
67
var headerReplyToExp = regexp.MustCompile("(^|\n)Reply-To: [^\r\n]*\r?\n")
68
var headerSenderExp = regexp.MustCompile("(^|\n)Sender: [^\r\n]*\r?\n")
69
var headerReturnPathExp = regexp.MustCompile("(^|\n)Return-Path: [^\r\n]*\r?\n")
70
var headerDKIMSigExp = regexp.MustCompile("(^|\n)DKIM-Signature: [^\r\n]*\r?\n")
71
var headerEndExp = regexp.MustCompile("((\r?\n){2})")
72

73
// Forward will try to forward the SES event to the given recipient
74
func (e *Event) Forward(session client.ConfigProvider, to string) error {
×
75
        sesClient := ses.New(session)
×
76
        _, err := sesClient.SendRawEmail(&ses.SendRawEmailInput{
×
77
                RawMessage: &ses.RawMessage{Data: generateMail(e.email, e.To, to)},
×
78
        })
×
79
        return err
×
80
}
×
81

82
func generateMail(raw []byte, originalTo, to string) []byte {
1✔
83
        fromMatches := headerFromExp.FindSubmatch(raw)
1✔
84
        if len(fromMatches) > 3 {
2✔
85
                from := fromMatches[2]
1✔
86
                ending := fromMatches[3]
1✔
87
                addHeader := func(in []byte, name string) []byte {
2✔
88
                        locs := headerEndExp.FindIndex(in)
1✔
89

1✔
90
                        outStart := in
1✔
91
                        outEnd := []byte{}
1✔
92
                        if len(locs) > 0 {
2✔
93
                                outStart = in[:locs[0]]
1✔
94
                                outEnd = in[locs[0]:]
1✔
95
                        }
1✔
96
                        header := append([]byte{}, ending...)
1✔
97
                        header = append(header, append([]byte(name+": "), from...)...)
1✔
98
                        result := append([]byte{}, outStart...)
1✔
99
                        return append(result, append(header, outEnd...)...)
1✔
100
                }
101

102
                if !headerReplyToExp.Match(raw) {
2✔
103
                        raw = addHeader(raw, "Reply-To")
1✔
104
                }
1✔
105
                raw = addHeader(raw, "X-Actual-From")
1✔
106
        }
107

108
        buggyRegexBS := " \r\n::OMG::\r\n "
1✔
109

1✔
110
        raw = headerFromExp.ReplaceAll(raw, []byte("$1"+buggyRegexBS+"From: "+originalTo+"$3"))
1✔
111
        raw = headerToExp.ReplaceAll(raw, []byte("$1"+buggyRegexBS+"To: "+to+"$2"))
1✔
112
        raw = headerReturnPathExp.ReplaceAll(raw, []byte("$1"))
1✔
113
        raw = headerSenderExp.ReplaceAll(raw, []byte("$1"))
1✔
114
        raw = headerDKIMSigExp.ReplaceAll(raw, []byte("$1"))
1✔
115

1✔
116
        raw = bytes.Replace(raw, []byte(buggyRegexBS), []byte{}, -1)
1✔
117

1✔
118
        return raw
1✔
119
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc