gophish/models/template_context.go

157 lines
4.2 KiB
Go

package models
import (
"bytes"
"encoding/json"
"net/mail"
"net/url"
"path"
"text/template"
)
// TemplateContext is an interface that allows both campaigns and email
// requests to have a PhishingTemplateContext generated for them.
type TemplateContext interface {
getFromAddress() string
getBaseURL() string
}
// PhishingTemplateContext is the context that is sent to any template, such
// as the email or landing page content.
type PhishingTemplateContext struct {
From string
URL string
Tracker string
TrackingURL string
RId string
BaseURL string
BaseRecipient
}
// NewPhishingTemplateContext returns a populated PhishingTemplateContext,
// parsing the correct fields from the provided TemplateContext and recipient.
func NewPhishingTemplateContext(ctx TemplateContext, r BaseRecipient, rid string) (PhishingTemplateContext, error) {
f, err := mail.ParseAddress(ctx.getFromAddress())
if err != nil {
return PhishingTemplateContext{}, err
}
fn := f.Name
if fn == "" {
fn = f.Address
}
templateURL, err := ExecuteTemplate(ctx.getBaseURL(), r)
if err != nil {
return PhishingTemplateContext{}, err
}
// For the base URL, we'll reset the the path and the query
// This will create a URL in the form of http://example.com
baseURL, err := url.Parse(templateURL)
if err != nil {
return PhishingTemplateContext{}, err
}
baseURL.Path = ""
baseURL.RawQuery = ""
phishURL, _ := url.Parse(templateURL)
q := phishURL.Query()
q.Set(RecipientParameter, rid)
phishURL.RawQuery = q.Encode()
trackingURL, _ := url.Parse(templateURL)
trackingURL.Path = path.Join(trackingURL.Path, "/track")
trackingURL.RawQuery = q.Encode()
return PhishingTemplateContext{
BaseRecipient: r,
BaseURL: baseURL.String(),
URL: phishURL.String(),
TrackingURL: trackingURL.String(),
Tracker: "<img alt='' style='display: none' src='" + trackingURL.String() + "'/>",
From: fn,
RId: rid,
}, nil
}
// ExecuteTemplate creates a templated string based on the provided
// template body and data.
func ExecuteTemplate(text string, data interface{}) (string, error) {
buff := bytes.Buffer{}
// replacing template params with no corresponding map keys with empty string.
tmpl, err := template.New("template").Option("missingkey=zero").Parse(text)
if err != nil {
return buff.String(), err
}
err = tmpl.Execute(&buff, data)
return buff.String(), err
}
// ValidationContext is used for validating templates and pages
type ValidationContext struct {
FromAddress string
BaseURL string
}
func (vc ValidationContext) getFromAddress() string {
return vc.FromAddress
}
func (vc ValidationContext) getBaseURL() string {
return vc.BaseURL
}
// ValidateTemplate ensures that the provided text in the page or template
// uses the supported template variables correctly.
func ValidateTemplate(text string) error {
vc := ValidationContext{
FromAddress: "foo@bar.com",
BaseURL: "http://example.com",
}
td := Result{
BaseRecipient: BaseRecipient{
Email: "foo@bar.com",
FirstName: "Foo",
LastName: "Bar",
Position: "Test",
},
RId: "123456",
}
ptx, err := NewPhishingTemplateContext(vc, td.BaseRecipient, td.RId)
if err != nil {
return err
}
_, err = ExecuteTemplate(text, ptx)
if err != nil {
return err
}
return nil
}
// GetAllTemplateParams merges the PhishingTemplateContext with the extended_parameters
// if extended_parameters are valid JSON
func GetAllTemplateParams(ptx PhishingTemplateContext) map[string]string {
currentTemplateParams := map[string]string{
"From": ptx.From,
"URL": ptx.URL,
"Tracker": ptx.Tracker,
"TrackingURL": ptx.TrackingURL,
"RId": ptx.RId,
"BaseURL": ptx.BaseURL,
"Email": ptx.BaseRecipient.Email,
"FirstName": ptx.BaseRecipient.FirstName,
"LastName": ptx.BaseRecipient.LastName,
"Position": ptx.BaseRecipient.Position,
}
extendedTemplateParams := map[string]string{}
err := json.Unmarshal([]byte(ptx.BaseRecipient.ExtendedTemplate), &extendedTemplateParams)
if err == nil {
// only merge if we have a valid JSON in extended_parameters
for k, v := range extendedTemplateParams {
currentTemplateParams[k] = v
}
}
return currentTemplateParams
}