github.com/bluestoneag/bluephish@v0.1.0/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/bluestoneag/bluephish/config" 14 "github.com/bluestoneag/bluephish/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 }