this repo has no description
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

security: add SSRF protection for URL fetching

Use doyensec/safeurl library to prevent server-side request forgery
when fetching URL previews. This blocks requests to private/internal
IP ranges and protects against DNS rebinding attacks. Links to
private addresses are still stored but won't have title previews.

Also adds io.LimitReader to cap response bodies at 1MB to prevent
memory exhaustion from large responses.

+21 -8
+1
go.mod
··· 13 13 require ( 14 14 filippo.io/edwards25519 v1.1.0 // indirect 15 15 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect 16 + github.com/doyensec/safeurl v0.2.2 // indirect 16 17 github.com/dustin/go-humanize v1.0.1 // indirect 17 18 github.com/fsnotify/fsnotify v1.9.0 // indirect 18 19 github.com/glebarez/go-sqlite v1.21.2 // indirect
+2
go.sum
··· 2 2 filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= 3 3 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= 4 4 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 + github.com/doyensec/safeurl v0.2.2 h1:+sFUqwOnqqmtUAC85/sGdOKfJh8zOacyghkaLzsOk40= 6 + github.com/doyensec/safeurl v0.2.2/go.mod h1:3H0cgRpPYPSpgxRRn5yGD35Ns/LgGX/BVWSBbzUqXtY= 5 7 github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= 6 8 github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= 7 9 github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
+18 -8
internal/handler/irclink.go
··· 4 4 "context" 5 5 "encoding/json" 6 6 "fmt" 7 + "io" 7 8 "log" 8 9 "net/http" 9 10 "strconv" 10 - 11 - "io/ioutil" 12 11 "strings" 13 12 "time" 13 + 14 + "github.com/doyensec/safeurl" 14 15 ) 15 16 16 17 // LinkSubmissionResponse represents the response when a link is submitted ··· 93 94 94 95 isDuplicate := len(existingLinks) > 0 95 96 96 - // Fetch title (simple impl) 97 + // Fetch title using SSRF-safe client 97 98 title := url // Default to URL 98 - client := &http.Client{Timeout: 10 * time.Second} 99 + contentType := "0" 100 + 101 + // Configure safeurl to block private/internal IPs and restrict schemes 102 + config := safeurl.GetConfigBuilder(). 103 + SetTimeout(10 * time.Second). 104 + Build() 105 + client := safeurl.Client(config) 106 + 99 107 resp, err := client.Get(url) 100 - contentType := "0" 101 108 if err == nil { 102 109 defer resp.Body.Close() 103 110 // Basic title extraction (should improve for production) 104 111 if strings.Contains(resp.Header.Get("Content-Type"), "image") { 105 112 contentType = "image" 106 113 } 107 - // Extract title logic omitted for brevity, using URL as fallback 108 - // In real impl, read body and regex <title> 109 - body, _ := ioutil.ReadAll(resp.Body) 114 + // Limit response body to 1MB to prevent memory exhaustion 115 + limitedBody := io.LimitReader(resp.Body, 1024*1024) 116 + body, _ := io.ReadAll(limitedBody) 110 117 if idx := strings.Index(string(body), "<title>"); idx != -1 { 111 118 end := strings.Index(string(body)[idx:], "</title>") 112 119 if end != -1 { 113 120 title = string(body)[idx+7 : idx+end] 114 121 } 115 122 } 123 + } else { 124 + // Log blocked URLs for debugging (private IPs, etc.) 125 + log.Printf("URL fetch blocked or failed for %s: %v", url, err) 116 126 } 117 127 118 128 // Insert the link (always insert, even if duplicate)