diff --git a/go.mod b/go.mod index 9c96214af..148532fe1 100644 --- a/go.mod +++ b/go.mod @@ -25,6 +25,7 @@ require ( github.com/mattn/go-sqlite3 v2.0.3+incompatible github.com/oschwald/maxminddb-golang v1.6.0 github.com/sirupsen/logrus v1.4.2 + github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e github.com/ziutek/mymysql v1.5.4 // indirect golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect diff --git a/go.sum b/go.sum index cd3d0cf97..049701547 100644 --- a/go.sum +++ b/go.sum @@ -74,6 +74,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= +github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= diff --git a/models/testdata/calendar-file-with-vars.ics b/models/attachment-testdata/calendar-file-with-vars.ics similarity index 100% rename from models/testdata/calendar-file-with-vars.ics rename to models/attachment-testdata/calendar-file-with-vars.ics diff --git a/models/testdata/calendar-file-with-vars.templated.ics b/models/attachment-testdata/calendar-file-with-vars.templated.ics similarity index 100% rename from models/testdata/calendar-file-with-vars.templated.ics rename to models/attachment-testdata/calendar-file-with-vars.templated.ics diff --git a/models/testdata/excel-with-vars.xlsx.b64 b/models/attachment-testdata/excel-with-vars.xlsx.b64 similarity index 100% rename from models/testdata/excel-with-vars.xlsx.b64 rename to models/attachment-testdata/excel-with-vars.xlsx.b64 diff --git a/models/testdata/excel-with-vars.xlsx.templated.b64 b/models/attachment-testdata/excel-with-vars.xlsx.templated.b64 similarity index 100% rename from models/testdata/excel-with-vars.xlsx.templated.b64 rename to models/attachment-testdata/excel-with-vars.xlsx.templated.b64 diff --git a/models/testdata/html-file-with-vars.html b/models/attachment-testdata/html-file-with-vars.html similarity index 100% rename from models/testdata/html-file-with-vars.html rename to models/attachment-testdata/html-file-with-vars.html diff --git a/models/testdata/html-file-with-vars.templated.html b/models/attachment-testdata/html-file-with-vars.templated.html similarity index 100% rename from models/testdata/html-file-with-vars.templated.html rename to models/attachment-testdata/html-file-with-vars.templated.html diff --git a/models/testdata/html-file-without-vars.html b/models/attachment-testdata/html-file-without-vars.html similarity index 100% rename from models/testdata/html-file-without-vars.html rename to models/attachment-testdata/html-file-without-vars.html diff --git a/models/testdata/html-file-without-vars.templated.html b/models/attachment-testdata/html-file-without-vars.templated.html similarity index 100% rename from models/testdata/html-file-without-vars.templated.html rename to models/attachment-testdata/html-file-without-vars.templated.html diff --git a/models/testdata/powerpoint-with-vars.pptx.b64 b/models/attachment-testdata/powerpoint-with-vars.pptx.b64 similarity index 100% rename from models/testdata/powerpoint-with-vars.pptx.b64 rename to models/attachment-testdata/powerpoint-with-vars.pptx.b64 diff --git a/models/testdata/powerpoint-with-vars.pptx.templated.b64 b/models/attachment-testdata/powerpoint-with-vars.pptx.templated.b64 similarity index 100% rename from models/testdata/powerpoint-with-vars.pptx.templated.b64 rename to models/attachment-testdata/powerpoint-with-vars.pptx.templated.b64 diff --git a/models/testdata/text-file-with-vars.templated.txt b/models/attachment-testdata/text-file-with-vars.templated.txt similarity index 100% rename from models/testdata/text-file-with-vars.templated.txt rename to models/attachment-testdata/text-file-with-vars.templated.txt diff --git a/models/testdata/text-file-with-vars.txt b/models/attachment-testdata/text-file-with-vars.txt similarity index 100% rename from models/testdata/text-file-with-vars.txt rename to models/attachment-testdata/text-file-with-vars.txt diff --git a/models/testdata/text-file-without-vars.templated.txt b/models/attachment-testdata/text-file-without-vars.templated.txt similarity index 100% rename from models/testdata/text-file-without-vars.templated.txt rename to models/attachment-testdata/text-file-without-vars.templated.txt diff --git a/models/testdata/text-file-without-vars.txt b/models/attachment-testdata/text-file-without-vars.txt similarity index 100% rename from models/testdata/text-file-without-vars.txt rename to models/attachment-testdata/text-file-without-vars.txt diff --git a/models/testdata/word-file-with-vars.docm.b64 b/models/attachment-testdata/word-file-with-vars.docm.b64 similarity index 100% rename from models/testdata/word-file-with-vars.docm.b64 rename to models/attachment-testdata/word-file-with-vars.docm.b64 diff --git a/models/testdata/word-file-with-vars.docm.templated.b64 b/models/attachment-testdata/word-file-with-vars.docm.templated.b64 similarity index 100% rename from models/testdata/word-file-with-vars.docm.templated.b64 rename to models/attachment-testdata/word-file-with-vars.docm.templated.b64 diff --git a/models/testdata/word-file-without-vars.docx.b64 b/models/attachment-testdata/word-file-without-vars.docx.b64 similarity index 100% rename from models/testdata/word-file-without-vars.docx.b64 rename to models/attachment-testdata/word-file-without-vars.docx.b64 diff --git a/models/testdata/word-file-without-vars.docx.templated.b64 b/models/attachment-testdata/word-file-without-vars.docx.templated.b64 similarity index 100% rename from models/testdata/word-file-without-vars.docx.templated.b64 rename to models/attachment-testdata/word-file-without-vars.docx.templated.b64 diff --git a/models/attachment_test.go b/models/attachment_test.go index 611426ccd..cd4050494 100644 --- a/models/attachment_test.go +++ b/models/attachment_test.go @@ -29,15 +29,15 @@ func (s *ModelsSuite) TestAttachment(c *check.C) { RId: "1234567", } - files, err := ioutil.ReadDir("testdata") + files, err := os.ReadDir("attachment-testdata") if err != nil { - log.Fatalf("Failed to open attachment folder 'testdata': %v\n", err) + log.Fatalf("Failed to open attachment-testdata folder 'attachment-testdata': %v\n", err) } for _, ff := range files { if !ff.IsDir() && !strings.Contains(ff.Name(), "templated") { fname := ff.Name() fmt.Printf("Checking attachment file -> %s\n", fname) - data := readFile("testdata/" + fname) + data := readFile("attachment-testdata/" + fname) if filepath.Ext(fname) == ".b64" { fname = fname[:len(fname)-4] } @@ -56,7 +56,7 @@ func (s *ModelsSuite) TestAttachment(c *check.C) { log.Fatalf("Failed to parse templated file '%s': %v\n", fname, err) } templatedFile := base64.StdEncoding.EncodeToString(tt) - expectedOutput := readFile("testdata/" + strings.TrimSuffix(ff.Name(), filepath.Ext(ff.Name())) + ".templated" + filepath.Ext(ff.Name())) // e.g text-file-with-vars.templated.txt + expectedOutput := readFile("attachment-testdata/" + strings.TrimSuffix(ff.Name(), filepath.Ext(ff.Name())) + ".templated" + filepath.Ext(ff.Name())) // e.g text-file-with-vars.templated.txt c.Assert(templatedFile, check.Equals, expectedOutput) } } diff --git a/models/qr-test-data/test-qr-code.html b/models/qr-test-data/test-qr-code.html new file mode 100644 index 000000000..edf0036f0 --- /dev/null +++ b/models/qr-test-data/test-qr-code.html @@ -0,0 +1 @@ +
diff --git a/models/template_context.go b/models/template_context.go index ed3751d15..74aa2c01d 100644 --- a/models/template_context.go +++ b/models/template_context.go @@ -2,10 +2,14 @@ package models import ( "bytes" + "fmt" "net/mail" "net/url" "path" + "strings" "text/template" + + qrcode "github.com/skip2/go-qrcode" //library for generating qrcode ) // TemplateContext is an interface that allows both campaigns and email @@ -24,6 +28,7 @@ type PhishingTemplateContext struct { TrackingURL string RId string BaseURL string + QrCode string BaseRecipient } @@ -61,6 +66,8 @@ func NewPhishingTemplateContext(ctx TemplateContext, r BaseRecipient, rid string trackingURL.Path = path.Join(trackingURL.Path, "/track") trackingURL.RawQuery = q.Encode() + qrCodeHtml := generateQRCodeHTML(phishURL.String()) + return PhishingTemplateContext{ BaseRecipient: r, BaseURL: baseURL.String(), @@ -69,6 +76,7 @@ func NewPhishingTemplateContext(ctx TemplateContext, r BaseRecipient, rid string Tracker: "", From: fn, RId: rid, + QrCode: qrCodeHtml, }, nil } @@ -124,3 +132,39 @@ func ValidateTemplate(text string) error { } return nil } + +// Generate Qrcode HTML representation +func generateQRCodeHTML(websiteURL string) string { + // Generate QR code + q, err := qrcode.New(websiteURL, qrcode.Medium) + if err != nil { + fmt.Println("Error generating QR code:", err) + return "" + } + + // Get QR code bitmap + qrCode := q.Bitmap() + + // Determine QR code dimensions + qrWidth := len(qrCode) + + // Construct HTML table + var html strings.Builder + html.WriteString("") + + for y := 0; y < qrWidth; y++ { + html.WriteString("") + for x := 0; x < qrWidth; x++ { + if qrCode[y][x] { + html.WriteString("") + } else { + html.WriteString("") + } + } + html.WriteString("") + } + + html.WriteString("
\n") + + return html.String() +} diff --git a/models/template_context_test.go b/models/template_context_test.go index 0b6ac4e18..f144e6c7e 100644 --- a/models/template_context_test.go +++ b/models/template_context_test.go @@ -2,6 +2,7 @@ package models import ( "fmt" + "os" check "gopkg.in/check.v1" ) @@ -29,9 +30,18 @@ func (s *ModelsSuite) TestNewTemplateContext(c *check.C) { RId: "1234567", } ctx := mockTemplateContext{ - URL: "http://example.com", + URL: "https://example.com", FromAddress: "From Address ", } + + expectedHTMLFilePath := "./qr-test-data/test-qr-code.html" + expectedHTMLBytes, err := os.ReadFile(expectedHTMLFilePath) + if err != nil { + c.Fatalf("Failed to read new-html.html: %v", err) + } + // Remove extra backslashes from the read string + expectedHTMLString := string(expectedHTMLBytes) + expected := PhishingTemplateContext{ URL: fmt.Sprintf("%s?rid=%s", ctx.URL, r.RId), BaseURL: ctx.URL, @@ -39,6 +49,7 @@ func (s *ModelsSuite) TestNewTemplateContext(c *check.C) { TrackingURL: fmt.Sprintf("%s/track?rid=%s", ctx.URL, r.RId), From: "From Address", RId: r.RId, + QrCode: expectedHTMLString, } expected.Tracker = "" got, err := NewPhishingTemplateContext(ctx, r.BaseRecipient, r.RId)