github.com/gophish/gophish@v0.12.2-0.20230915144530-8e7929441393/controllers/phish_test.go (about)

     1  package controllers
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"net/http"
     9  	"net/url"
    10  	"reflect"
    11  	"testing"
    12  
    13  	"github.com/gophish/gophish/config"
    14  	"github.com/gophish/gophish/models"
    15  )
    16  
    17  func getFirstCampaign(t *testing.T) models.Campaign {
    18  	campaigns, err := models.GetCampaigns(1)
    19  	if err != nil {
    20  		t.Fatalf("error getting first campaign from database: %v", err)
    21  	}
    22  	return campaigns[0]
    23  }
    24  
    25  func getFirstEmailRequest(t *testing.T) models.EmailRequest {
    26  	campaign := getFirstCampaign(t)
    27  	req := models.EmailRequest{
    28  		TemplateId:    campaign.TemplateId,
    29  		Template:      campaign.Template,
    30  		PageId:        campaign.PageId,
    31  		Page:          campaign.Page,
    32  		URL:           "http://localhost.localdomain",
    33  		UserId:        1,
    34  		BaseRecipient: campaign.Results[0].BaseRecipient,
    35  		SMTP:          campaign.SMTP,
    36  		FromAddress:   campaign.SMTP.FromAddress,
    37  	}
    38  	err := models.PostEmailRequest(&req)
    39  	if err != nil {
    40  		t.Fatalf("error creating email request: %v", err)
    41  	}
    42  	return req
    43  }
    44  
    45  func openEmail(t *testing.T, ctx *testContext, rid string) {
    46  	resp, err := http.Get(fmt.Sprintf("%s/track?%s=%s", ctx.phishServer.URL, models.RecipientParameter, rid))
    47  	if err != nil {
    48  		t.Fatalf("error requesting /track endpoint: %v", err)
    49  	}
    50  	defer resp.Body.Close()
    51  	got, err := ioutil.ReadAll(resp.Body)
    52  	if err != nil {
    53  		t.Fatalf("error reading response body from /track endpoint: %v", err)
    54  	}
    55  	expected, err := ioutil.ReadFile("static/images/pixel.png")
    56  	if err != nil {
    57  		t.Fatalf("error reading local transparent pixel: %v", err)
    58  	}
    59  	if !bytes.Equal(got, expected) {
    60  		t.Fatalf("unexpected tracking pixel data received. expected %#v got %#v", expected, got)
    61  	}
    62  }
    63  
    64  func openEmail404(t *testing.T, ctx *testContext, rid string) {
    65  	resp, err := http.Get(fmt.Sprintf("%s/track?%s=%s", ctx.phishServer.URL, models.RecipientParameter, rid))
    66  	if err != nil {
    67  		t.Fatalf("error requesting /track endpoint: %v", err)
    68  	}
    69  	defer resp.Body.Close()
    70  	got := resp.StatusCode
    71  	expected := http.StatusNotFound
    72  	if got != expected {
    73  		t.Fatalf("invalid status code received for /track endpoint. expected %d got %d", expected, got)
    74  	}
    75  }
    76  
    77  func reportedEmail(t *testing.T, ctx *testContext, rid string) {
    78  	resp, err := http.Get(fmt.Sprintf("%s/report?%s=%s", ctx.phishServer.URL, models.RecipientParameter, rid))
    79  	if err != nil {
    80  		t.Fatalf("error requesting /report endpoint: %v", err)
    81  	}
    82  	got := resp.StatusCode
    83  	expected := http.StatusNoContent
    84  	if got != expected {
    85  		t.Fatalf("invalid status code received for /report endpoint. expected %d got %d", expected, got)
    86  	}
    87  }
    88  
    89  func reportEmail404(t *testing.T, ctx *testContext, rid string) {
    90  	resp, err := http.Get(fmt.Sprintf("%s/report?%s=%s", ctx.phishServer.URL, models.RecipientParameter, rid))
    91  	if err != nil {
    92  		t.Fatalf("error requesting /report endpoint: %v", err)
    93  	}
    94  	got := resp.StatusCode
    95  	expected := http.StatusNotFound
    96  	if got != expected {
    97  		t.Fatalf("invalid status code received for /report endpoint. expected %d got %d", expected, got)
    98  	}
    99  }
   100  
   101  func clickLink(t *testing.T, ctx *testContext, rid string, expectedHTML string) {
   102  	resp, err := http.Get(fmt.Sprintf("%s/?%s=%s", ctx.phishServer.URL, models.RecipientParameter, rid))
   103  	if err != nil {
   104  		t.Fatalf("error requesting / endpoint: %v", err)
   105  	}
   106  	defer resp.Body.Close()
   107  	got, err := ioutil.ReadAll(resp.Body)
   108  	if err != nil {
   109  		t.Fatalf("error reading payload from / endpoint response: %v", err)
   110  	}
   111  	if !bytes.Equal(got, []byte(expectedHTML)) {
   112  		t.Fatalf("invalid response received from / endpoint. expected %s got %s", got, expectedHTML)
   113  	}
   114  }
   115  
   116  func clickLink404(t *testing.T, ctx *testContext, rid string) {
   117  	resp, err := http.Get(fmt.Sprintf("%s/?%s=%s", ctx.phishServer.URL, models.RecipientParameter, rid))
   118  	if err != nil {
   119  		t.Fatalf("error requesting / endpoint: %v", err)
   120  	}
   121  	defer resp.Body.Close()
   122  	got := resp.StatusCode
   123  	expected := http.StatusNotFound
   124  	if got != expected {
   125  		t.Fatalf("invalid status code received for / endpoint. expected %d got %d", expected, got)
   126  	}
   127  }
   128  
   129  func transparencyRequest(t *testing.T, ctx *testContext, r models.Result, rid, path string) {
   130  	resp, err := http.Get(fmt.Sprintf("%s%s?%s=%s", ctx.phishServer.URL, path, models.RecipientParameter, rid))
   131  	if err != nil {
   132  		t.Fatalf("error requesting %s endpoint: %v", path, err)
   133  	}
   134  	defer resp.Body.Close()
   135  	got := resp.StatusCode
   136  	expected := http.StatusOK
   137  	if got != expected {
   138  		t.Fatalf("invalid status code received for / endpoint. expected %d got %d", expected, got)
   139  	}
   140  	tr := &TransparencyResponse{}
   141  	err = json.NewDecoder(resp.Body).Decode(tr)
   142  	if err != nil {
   143  		t.Fatalf("error unmarshaling transparency request: %v", err)
   144  	}
   145  	expectedResponse := &TransparencyResponse{
   146  		ContactAddress: ctx.config.ContactAddress,
   147  		SendDate:       r.SendDate,
   148  		Server:         config.ServerName,
   149  	}
   150  	if !reflect.DeepEqual(tr, expectedResponse) {
   151  		t.Fatalf("unexpected transparency response received. expected %v got %v", expectedResponse, tr)
   152  	}
   153  }
   154  
   155  func TestOpenedPhishingEmail(t *testing.T) {
   156  	ctx := setupTest(t)
   157  	defer tearDown(t, ctx)
   158  	campaign := getFirstCampaign(t)
   159  	result := campaign.Results[0]
   160  	if result.Status != models.StatusSending {
   161  		t.Fatalf("unexpected result status received. expected %s got %s", models.StatusSending, result.Status)
   162  	}
   163  
   164  	openEmail(t, ctx, result.RId)
   165  
   166  	campaign = getFirstCampaign(t)
   167  	result = campaign.Results[0]
   168  	lastEvent := campaign.Events[len(campaign.Events)-1]
   169  	if result.Status != models.EventOpened {
   170  		t.Fatalf("unexpected result status received. expected %s got %s", models.EventOpened, result.Status)
   171  	}
   172  	if lastEvent.Message != models.EventOpened {
   173  		t.Fatalf("unexpected event status received. expected %s got %s", lastEvent.Message, models.EventOpened)
   174  	}
   175  	if result.ModifiedDate != lastEvent.Time {
   176  		t.Fatalf("unexpected result modified date received. expected %s got %s", lastEvent.Time, result.ModifiedDate)
   177  	}
   178  }
   179  
   180  func TestReportedPhishingEmail(t *testing.T) {
   181  	ctx := setupTest(t)
   182  	defer tearDown(t, ctx)
   183  	campaign := getFirstCampaign(t)
   184  	result := campaign.Results[0]
   185  	if result.Status != models.StatusSending {
   186  		t.Fatalf("unexpected result status received. expected %s got %s", models.StatusSending, result.Status)
   187  	}
   188  
   189  	reportedEmail(t, ctx, result.RId)
   190  
   191  	campaign = getFirstCampaign(t)
   192  	result = campaign.Results[0]
   193  	lastEvent := campaign.Events[len(campaign.Events)-1]
   194  
   195  	if result.Reported != true {
   196  		t.Fatalf("unexpected result report status received. expected %v got %v", true, result.Reported)
   197  	}
   198  	if lastEvent.Message != models.EventReported {
   199  		t.Fatalf("unexpected event status received. expected %s got %s", lastEvent.Message, models.EventReported)
   200  	}
   201  	if result.ModifiedDate != lastEvent.Time {
   202  		t.Fatalf("unexpected result modified date received. expected %s got %s", lastEvent.Time, result.ModifiedDate)
   203  	}
   204  }
   205  
   206  func TestClickedPhishingLinkAfterOpen(t *testing.T) {
   207  	ctx := setupTest(t)
   208  	defer tearDown(t, ctx)
   209  	campaign := getFirstCampaign(t)
   210  	result := campaign.Results[0]
   211  	if result.Status != models.StatusSending {
   212  		t.Fatalf("unexpected result status received. expected %s got %s", models.StatusSending, result.Status)
   213  	}
   214  
   215  	openEmail(t, ctx, result.RId)
   216  	clickLink(t, ctx, result.RId, campaign.Page.HTML)
   217  
   218  	campaign = getFirstCampaign(t)
   219  	result = campaign.Results[0]
   220  	lastEvent := campaign.Events[len(campaign.Events)-1]
   221  	if result.Status != models.EventClicked {
   222  		t.Fatalf("unexpected result status received. expected %s got %s", models.EventClicked, result.Status)
   223  	}
   224  	if lastEvent.Message != models.EventClicked {
   225  		t.Fatalf("unexpected event status received. expected %s got %s", lastEvent.Message, models.EventClicked)
   226  	}
   227  	if result.ModifiedDate != lastEvent.Time {
   228  		t.Fatalf("unexpected result modified date received. expected %s got %s", lastEvent.Time, result.ModifiedDate)
   229  	}
   230  }
   231  
   232  func TestNoRecipientID(t *testing.T) {
   233  	ctx := setupTest(t)
   234  	defer tearDown(t, ctx)
   235  	resp, err := http.Get(fmt.Sprintf("%s/track", ctx.phishServer.URL))
   236  	if err != nil {
   237  		t.Fatalf("error requesting /track endpoint: %v", err)
   238  	}
   239  	got := resp.StatusCode
   240  	expected := http.StatusNotFound
   241  	if got != expected {
   242  		t.Fatalf("invalid status code received for /track endpoint. expected %d got %d", expected, got)
   243  	}
   244  
   245  	resp, err = http.Get(ctx.phishServer.URL)
   246  	if err != nil {
   247  		t.Fatalf("error requesting /track endpoint: %v", err)
   248  	}
   249  	got = resp.StatusCode
   250  	if got != expected {
   251  		t.Fatalf("invalid status code received for / endpoint. expected %d got %d", expected, got)
   252  	}
   253  }
   254  
   255  func TestInvalidRecipientID(t *testing.T) {
   256  	ctx := setupTest(t)
   257  	defer tearDown(t, ctx)
   258  	rid := "XXXXXXXXXX"
   259  	openEmail404(t, ctx, rid)
   260  	clickLink404(t, ctx, rid)
   261  	reportEmail404(t, ctx, rid)
   262  }
   263  
   264  func TestCompletedCampaignClick(t *testing.T) {
   265  	ctx := setupTest(t)
   266  	defer tearDown(t, ctx)
   267  	campaign := getFirstCampaign(t)
   268  	result := campaign.Results[0]
   269  	if result.Status != models.StatusSending {
   270  		t.Fatalf("unexpected result status received. expected %s got %s", models.StatusSending, result.Status)
   271  	}
   272  
   273  	openEmail(t, ctx, result.RId)
   274  
   275  	campaign = getFirstCampaign(t)
   276  	result = campaign.Results[0]
   277  	if result.Status != models.EventOpened {
   278  		t.Fatalf("unexpected result status received. expected %s got %s", models.EventOpened, result.Status)
   279  	}
   280  
   281  	models.CompleteCampaign(campaign.Id, 1)
   282  	openEmail404(t, ctx, result.RId)
   283  	clickLink404(t, ctx, result.RId)
   284  
   285  	campaign = getFirstCampaign(t)
   286  	result = campaign.Results[0]
   287  	if result.Status != models.EventOpened {
   288  		t.Fatalf("unexpected result status received. expected %s got %s", models.EventOpened, result.Status)
   289  	}
   290  }
   291  
   292  func TestRobotsHandler(t *testing.T) {
   293  	ctx := setupTest(t)
   294  	defer tearDown(t, ctx)
   295  	resp, err := http.Get(fmt.Sprintf("%s/robots.txt", ctx.phishServer.URL))
   296  	if err != nil {
   297  		t.Fatalf("error requesting /robots.txt endpoint: %v", err)
   298  	}
   299  	defer resp.Body.Close()
   300  	got := resp.StatusCode
   301  	expectedStatus := http.StatusOK
   302  	if got != expectedStatus {
   303  		t.Fatalf("invalid status code received for /track endpoint. expected %d got %d", expectedStatus, got)
   304  	}
   305  	expected := []byte("User-agent: *\nDisallow: /\n")
   306  	body, err := ioutil.ReadAll(resp.Body)
   307  	if err != nil {
   308  		t.Fatalf("error reading response body from /robots.txt endpoint: %v", err)
   309  	}
   310  	if !bytes.Equal(body, expected) {
   311  		t.Fatalf("invalid robots.txt response received. expected %s got %s", expected, body)
   312  	}
   313  }
   314  
   315  func TestInvalidPreviewID(t *testing.T) {
   316  	ctx := setupTest(t)
   317  	defer tearDown(t, ctx)
   318  	bogusRId := fmt.Sprintf("%sbogus", models.PreviewPrefix)
   319  	openEmail404(t, ctx, bogusRId)
   320  	clickLink404(t, ctx, bogusRId)
   321  	reportEmail404(t, ctx, bogusRId)
   322  }
   323  
   324  func TestPreviewTrack(t *testing.T) {
   325  	ctx := setupTest(t)
   326  	defer tearDown(t, ctx)
   327  	req := getFirstEmailRequest(t)
   328  	openEmail(t, ctx, req.RId)
   329  }
   330  
   331  func TestPreviewClick(t *testing.T) {
   332  	ctx := setupTest(t)
   333  	defer tearDown(t, ctx)
   334  	req := getFirstEmailRequest(t)
   335  	clickLink(t, ctx, req.RId, req.Page.HTML)
   336  }
   337  
   338  func TestInvalidTransparencyRequest(t *testing.T) {
   339  	ctx := setupTest(t)
   340  	defer tearDown(t, ctx)
   341  	bogusRId := fmt.Sprintf("bogus%s", TransparencySuffix)
   342  	openEmail404(t, ctx, bogusRId)
   343  	clickLink404(t, ctx, bogusRId)
   344  	reportEmail404(t, ctx, bogusRId)
   345  }
   346  
   347  func TestTransparencyRequest(t *testing.T) {
   348  	ctx := setupTest(t)
   349  	defer tearDown(t, ctx)
   350  	campaign := getFirstCampaign(t)
   351  	result := campaign.Results[0]
   352  	rid := fmt.Sprintf("%s%s", result.RId, TransparencySuffix)
   353  	transparencyRequest(t, ctx, result, rid, "/")
   354  	transparencyRequest(t, ctx, result, rid, "/track")
   355  	transparencyRequest(t, ctx, result, rid, "/report")
   356  
   357  	// And check with the URL encoded version of a +
   358  	rid = fmt.Sprintf("%s%s", result.RId, "%2b")
   359  	transparencyRequest(t, ctx, result, rid, "/")
   360  	transparencyRequest(t, ctx, result, rid, "/track")
   361  	transparencyRequest(t, ctx, result, rid, "/report")
   362  }
   363  
   364  func TestRedirectTemplating(t *testing.T) {
   365  	ctx := setupTest(t)
   366  	defer tearDown(t, ctx)
   367  	p := models.Page{
   368  		Name:        "Redirect Page",
   369  		HTML:        "<html>Test</html>",
   370  		UserId:      1,
   371  		RedirectURL: "http://example.com/{{.RId}}",
   372  	}
   373  	err := models.PostPage(&p)
   374  	if err != nil {
   375  		t.Fatalf("error posting new page: %v", err)
   376  	}
   377  	smtp, _ := models.GetSMTP(1, 1)
   378  	template, _ := models.GetTemplate(1, 1)
   379  	group, _ := models.GetGroup(1, 1)
   380  
   381  	campaign := models.Campaign{Name: "Redirect campaign"}
   382  	campaign.UserId = 1
   383  	campaign.Template = template
   384  	campaign.Page = p
   385  	campaign.SMTP = smtp
   386  	campaign.Groups = []models.Group{group}
   387  	err = models.PostCampaign(&campaign, campaign.UserId)
   388  	if err != nil {
   389  		t.Fatalf("error creating campaign: %v", err)
   390  	}
   391  
   392  	client := http.Client{
   393  		CheckRedirect: func(req *http.Request, via []*http.Request) error {
   394  			return http.ErrUseLastResponse
   395  		},
   396  	}
   397  	result := campaign.Results[0]
   398  	resp, err := client.PostForm(fmt.Sprintf("%s/?%s=%s", ctx.phishServer.URL, models.RecipientParameter, result.RId), url.Values{"username": {"test"}, "password": {"test"}})
   399  	if err != nil {
   400  		t.Fatalf("error requesting / endpoint: %v", err)
   401  	}
   402  	defer resp.Body.Close()
   403  	got := resp.StatusCode
   404  	expectedStatus := http.StatusFound
   405  	if got != expectedStatus {
   406  		t.Fatalf("invalid status code received for /track endpoint. expected %d got %d", expectedStatus, got)
   407  	}
   408  	expectedURL := fmt.Sprintf("http://example.com/%s", result.RId)
   409  	gotURL, err := resp.Location()
   410  	if err != nil {
   411  		t.Fatalf("error getting Location header from response: %v", err)
   412  	}
   413  	if gotURL.String() != expectedURL {
   414  		t.Fatalf("invalid redirect received. expected %s got %s", expectedURL, gotURL)
   415  	}
   416  }