- Timestamp:
- Dec 1, 2016, 12:45:38 PM (9 years ago)
- Location:
- trunk
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/morty.go
r59 r60 15 15 "strings" 16 16 "time" 17 "unicode/utf8" 17 18 18 19 "github.com/valyala/fasthttp" … … 208 209 209 210 req.SetRequestURI(requestURIStr) 210 req.Header.SetUserAgentBytes([]byte("Mozilla/5.0 (Windows NT 10.0; WOW64; rv:50.0) Gecko/20100101 Firefox/50.0"))211 req.Header.SetUserAgentBytes([]byte("Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36")) 211 212 212 213 resp := fasthttp.AcquireResponse() … … 237 238 if loc != nil { 238 239 rc := &RequestConfig{Key: p.Key, BaseURL: parsedURI} 239 url, err := rc.ProxifyURI( string(loc))240 url, err := rc.ProxifyURI(loc) 240 241 if err == nil { 241 242 ctx.SetStatusCode(resp.StatusCode()) … … 346 347 urlEnd := s[5] 347 348 348 if uri, err := rc.ProxifyURI( string(css[urlStart:urlEnd])); err == nil {349 if uri, err := rc.ProxifyURI(css[urlStart:urlEnd]); err == nil { 349 350 out.Write(css[startIndex:urlStart]) 350 351 out.Write([]byte(uri)) … … 500 501 } 501 502 502 case html.CommentToken: 503 // ignore comment. TODO : parse IE conditional comment 504 505 case html.DoctypeToken: 503 case html.DoctypeToken, html.CommentToken: 506 504 out.Write(decoder.Raw()) 507 505 } … … 586 584 } 587 585 // output proxify result 588 if uri, err := rc.ProxifyURI( string(contentUrl)); err == nil {586 if uri, err := rc.ProxifyURI(contentUrl); err == nil { 589 587 fmt.Fprintf(out, ` http-equiv="refresh" content="%surl=%s"`, content[:urlIndex], uri) 590 588 } … … 611 609 switch string(attrName) { 612 610 case "src", "href", "action": 613 if uri, err := rc.ProxifyURI( string(attrValue)); err == nil {611 if uri, err := rc.ProxifyURI(attrValue); err == nil { 614 612 fmt.Fprintf(out, " %s=\"%s\"", attrName, uri) 615 613 } else { … … 627 625 } 628 626 629 func (rc *RequestConfig) ProxifyURI(uri string) (string, error) { 627 // Sanitized URI : removes all runes bellow 32 (included) as the begining and end of URI, and lower case the scheme. 628 // avoid memory allocation (except for the scheme) 629 func sanitizeURI(uri []byte) ([]byte, string) { 630 first_rune_index := 0 631 first_rune_seen := false 632 scheme_last_index := -1 633 buffer := bytes.NewBuffer(make([]byte, 0, 10)) 634 635 // remove trailing space and special characters 636 uri = bytes.TrimRight(uri, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x20") 637 638 // loop over byte by byte 639 for i, c := range uri { 640 // ignore special characters and space (c <= 32) 641 if c > 32 { 642 // append to the lower case of the rune to buffer 643 if c < utf8.RuneSelf && 'A' <= c && c <= 'Z' { 644 c = c + 'a' - 'A' 645 } 646 647 buffer.WriteByte(c) 648 649 // update the first rune index that is not a special rune 650 if !first_rune_seen { 651 first_rune_index = i 652 first_rune_seen = true 653 } 654 655 if c == ':' { 656 // colon rune found, we have found the scheme 657 scheme_last_index = i 658 break 659 } else if c == '/' || c == '?' || c == '\\' || c == '#' { 660 // special case : most probably a relative URI 661 break 662 } 663 } 664 } 665 666 if scheme_last_index != -1 { 667 // scheme found 668 // copy the "lower case without special runes scheme" before the ":" rune 669 scheme_start_index := scheme_last_index - buffer.Len() + 1 670 copy(uri[scheme_start_index:], buffer.Bytes()) 671 // and return the result 672 return uri[scheme_start_index:], buffer.String() 673 } else { 674 // scheme NOT found 675 return uri[first_rune_index:], "" 676 } 677 } 678 679 func (rc *RequestConfig) ProxifyURI(uri []byte) (string, error) { 680 // sanitize URI 681 uri, scheme := sanitizeURI(uri) 682 630 683 // remove javascript protocol 631 if s trings.HasPrefix(uri, "javascript:"){684 if scheme == "javascript:" { 632 685 return "", nil 633 686 } 634 687 635 688 // TODO check malicious data: - e.g. data:script 636 if s trings.HasPrefix(uri, "data:"){637 return uri, nil689 if scheme == "data:" { 690 return string(uri), nil 638 691 } 639 692 640 693 // parse the uri 641 u, err := url.Parse( uri)694 u, err := url.Parse(string(uri)) 642 695 if err != nil { 643 696 return "", err … … 668 721 669 722 // return full URI and fragment (if not empty) 670 uri= u.String()723 morty_uri := u.String() 671 724 672 725 if rc.Key == nil { 673 return fmt.Sprintf("./?mortyurl=%s%s", url.QueryEscape( uri), fragment), nil674 } 675 return fmt.Sprintf("./?mortyhash=%s&mortyurl=%s%s", hash( uri, rc.Key), url.QueryEscape(uri), fragment), nil726 return fmt.Sprintf("./?mortyurl=%s%s", url.QueryEscape(morty_uri), fragment), nil 727 } 728 return fmt.Sprintf("./?mortyhash=%s&mortyurl=%s%s", hash(morty_uri, rc.Key), url.QueryEscape(morty_uri), fragment), nil 676 729 } 677 730 -
trunk/morty_test.go
r55 r60 13 13 } 14 14 15 type SanitizeURITestCase struct { 16 Input []byte 17 ExpectedOutput []byte 18 ExpectedScheme string 19 } 20 15 21 type StringTestCase struct { 16 22 Input string … … 38 44 []byte("console.log(document.cookies)"), 39 45 nil, 46 }, 47 } 48 49 var sanitizeUriTestData []*SanitizeURITestCase = []*SanitizeURITestCase{ 50 &SanitizeURITestCase{ 51 []byte("http://example.com/"), 52 []byte("http://example.com/"), 53 "http:", 54 }, 55 &SanitizeURITestCase{ 56 []byte("HtTPs://example.com/ \t"), 57 []byte("https://example.com/"), 58 "https:", 59 }, 60 &SanitizeURITestCase{ 61 []byte(" Ht TPs://example.com/ \t"), 62 []byte("https://example.com/"), 63 "https:", 64 }, 65 &SanitizeURITestCase{ 66 []byte("javascript:void(0)"), 67 []byte("javascript:void(0)"), 68 "javascript:", 69 }, 70 &SanitizeURITestCase{ 71 []byte(" /path/to/a/file/without/protocol "), 72 []byte("/path/to/a/file/without/protocol"), 73 "", 74 }, 75 &SanitizeURITestCase{ 76 []byte(" #fragment "), 77 []byte("#fragment"), 78 "", 79 }, 80 &SanitizeURITestCase{ 81 []byte(" qwertyuiop "), 82 []byte("qwertyuiop"), 83 "", 84 }, 85 &SanitizeURITestCase{ 86 []byte(""), 87 []byte(""), 88 "", 89 }, 90 &SanitizeURITestCase{ 91 []byte(":"), 92 []byte(":"), 93 ":", 94 }, 95 &SanitizeURITestCase{ 96 []byte(" :"), 97 []byte(":"), 98 ":", 99 }, 100 &SanitizeURITestCase{ 101 []byte("schéma:"), 102 []byte("schéma:"), 103 "schéma:", 40 104 }, 41 105 } … … 75 139 } 76 140 141 func TestSanitizeURI(t *testing.T) { 142 for _, testCase := range sanitizeUriTestData { 143 newUrl, scheme := sanitizeURI(testCase.Input) 144 if !bytes.Equal(newUrl, testCase.ExpectedOutput) { 145 t.Errorf( 146 `URL proxifier error. Expected: "%s", Got: "%s"`, 147 testCase.ExpectedOutput, 148 newUrl, 149 ) 150 } 151 if scheme != testCase.ExpectedScheme { 152 t.Errorf( 153 `URL proxifier error. Expected: "%s", Got: "%s"`, 154 testCase.ExpectedScheme, 155 scheme, 156 ) 157 } 158 } 159 } 160 77 161 func TestURLProxifier(t *testing.T) { 78 162 u, _ := url.Parse("http://127.0.0.1/") 79 163 rc := &RequestConfig{BaseURL: u} 80 164 for _, testCase := range urlTestData { 81 newUrl, err := rc.ProxifyURI( testCase.Input)165 newUrl, err := rc.ProxifyURI([]byte(testCase.Input)) 82 166 if err != nil { 83 167 t.Errorf("Failed to parse URL: %s", testCase.Input)
Note:
See TracChangeset
for help on using the changeset viewer.