|
1 | 1 | package ui |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "net/http" |
4 | 5 | "reflect" |
5 | 6 | "strings" |
6 | 7 | "testing" |
@@ -617,3 +618,116 @@ func TestReplyAllExcludesAllOwnAddresses(t *testing.T) { |
617 | 618 | }) |
618 | 619 | } |
619 | 620 | } |
| 621 | + |
| 622 | +func TestIsMimeMismatch(t *testing.T) { |
| 623 | + tests := []struct { |
| 624 | + name string |
| 625 | + ext string |
| 626 | + detected string |
| 627 | + want bool |
| 628 | + }{ |
| 629 | + // Disguised files — should be flagged |
| 630 | + {"sh disguised as png", ".png", "text/plain; charset=utf-8", true}, |
| 631 | + {"html disguised as jpg", ".jpg", "text/html; charset=utf-8", true}, |
| 632 | + {"elf binary as pdf", ".pdf", "application/octet-stream", true}, |
| 633 | + {"script as gif", ".gif", "text/plain; charset=utf-8", true}, |
| 634 | + |
| 635 | + // Legitimate files — should pass |
| 636 | + {"real png", ".png", "image/png", false}, |
| 637 | + {"real jpg", ".jpg", "image/jpeg", false}, |
| 638 | + {"real gif", ".gif", "image/gif", false}, |
| 639 | + {"real pdf", ".pdf", "application/pdf", false}, |
| 640 | + {"real zip", ".zip", "application/zip", false}, |
| 641 | + {"real mp3", ".mp3", "audio/mpeg", false}, |
| 642 | + {"real mp4", ".mp4", "video/mp4", false}, |
| 643 | + |
| 644 | + // Unknown extensions — can't validate, should pass through |
| 645 | + {"unknown ext .xyz", ".xyz", "text/plain; charset=utf-8", false}, |
| 646 | + {"unknown ext .foo", ".foo", "application/octet-stream", false}, |
| 647 | + } |
| 648 | + for _, tt := range tests { |
| 649 | + t.Run(tt.name, func(t *testing.T) { |
| 650 | + got := isMimeMismatch(tt.ext, tt.detected) |
| 651 | + if got != tt.want { |
| 652 | + t.Errorf("isMimeMismatch(%q, %q) = %v, want %v", tt.ext, tt.detected, got, tt.want) |
| 653 | + } |
| 654 | + }) |
| 655 | + } |
| 656 | +} |
| 657 | + |
| 658 | +func TestDangerousExts(t *testing.T) { |
| 659 | + // Verify known dangerous extensions are in the blocklist. |
| 660 | + dangerous := []string{".sh", ".exe", ".desktop", ".bat", ".py", ".jar", ".ps1"} |
| 661 | + for _, ext := range dangerous { |
| 662 | + if !dangerousExts[ext] { |
| 663 | + t.Errorf("expected %q in dangerousExts", ext) |
| 664 | + } |
| 665 | + } |
| 666 | + // Verify safe extensions are NOT in the blocklist. |
| 667 | + safe := []string{".png", ".jpg", ".pdf", ".txt", ".html", ".md"} |
| 668 | + for _, ext := range safe { |
| 669 | + if dangerousExts[ext] { |
| 670 | + t.Errorf("unexpected %q in dangerousExts", ext) |
| 671 | + } |
| 672 | + } |
| 673 | +} |
| 674 | + |
| 675 | +func TestMimeMismatchWithRealBytes(t *testing.T) { |
| 676 | + // Simulate real magic-byte detection scenarios using net/http.DetectContentType. |
| 677 | + tests := []struct { |
| 678 | + name string |
| 679 | + ext string |
| 680 | + data []byte // fake file content |
| 681 | + wantBad bool |
| 682 | + }{ |
| 683 | + { |
| 684 | + name: "bash script disguised as .png", |
| 685 | + ext: ".png", |
| 686 | + data: []byte("#!/bin/bash\necho hello\n"), |
| 687 | + wantBad: true, |
| 688 | + }, |
| 689 | + { |
| 690 | + name: "python script disguised as .jpg", |
| 691 | + ext: ".jpg", |
| 692 | + data: []byte("#!/usr/bin/env python3\nprint('hello')\n"), |
| 693 | + wantBad: true, |
| 694 | + }, |
| 695 | + { |
| 696 | + name: "html with script disguised as .pdf", |
| 697 | + ext: ".pdf", |
| 698 | + data: []byte("<html><body>harmless content</body></html>"), |
| 699 | + wantBad: true, |
| 700 | + }, |
| 701 | + { |
| 702 | + name: "real PNG file (magic bytes)", |
| 703 | + ext: ".png", |
| 704 | + // PNG magic: 89 50 4E 47 0D 0A 1A 0A |
| 705 | + data: []byte{0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00}, |
| 706 | + wantBad: false, |
| 707 | + }, |
| 708 | + { |
| 709 | + name: "real PDF file (magic bytes)", |
| 710 | + ext: ".pdf", |
| 711 | + // PDF magic: %PDF- |
| 712 | + data: []byte("%PDF-1.4 fake pdf content here"), |
| 713 | + wantBad: false, |
| 714 | + }, |
| 715 | + { |
| 716 | + name: "real GIF file (magic bytes)", |
| 717 | + ext: ".gif", |
| 718 | + // GIF magic: GIF89a |
| 719 | + data: []byte("GIF89a" + strings.Repeat("\x00", 100)), |
| 720 | + wantBad: false, |
| 721 | + }, |
| 722 | + } |
| 723 | + for _, tt := range tests { |
| 724 | + t.Run(tt.name, func(t *testing.T) { |
| 725 | + detected := http.DetectContentType(tt.data) |
| 726 | + got := isMimeMismatch(tt.ext, detected) |
| 727 | + if got != tt.wantBad { |
| 728 | + t.Errorf("isMimeMismatch(%q, %q) = %v, want %v (data: %q...)", |
| 729 | + tt.ext, detected, got, tt.wantBad, string(tt.data[:min(20, len(tt.data))])) |
| 730 | + } |
| 731 | + }) |
| 732 | + } |
| 733 | +} |
0 commit comments