Skip to content

Commit 7b82f05

Browse files
authored
Merge pull request #84 from drduh/21dec25
more sanitization and tests
2 parents 37505f3 + 5aa2006 commit 7b82f05

File tree

17 files changed

+250
-106
lines changed

17 files changed

+250
-106
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
.DS_Store
22
*.log
3+
main
34
release
45
testCoverage*

Makefile

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,10 @@ BUILDFLAG = \
3232
-X "$(BUILDPKG).Version=$(VERSION)"
3333
BUILDCMD = $(GO) build -ldflags '-s -w $(BUILDFLAG)'
3434
BINNAME = $(APPNAME)-$(BUILDOS)-$(BUILDARCH)-$(VERSION)
35-
GOBUILD = GOOS=$(BUILDOS) GOARCH=$(BUILDARCH) \
36-
$(BUILDCMD) -o $(OUT)/$(BINNAME) $(SRC)
35+
GOBUILD = GOOS=$(BUILDOS) GOARCH=$(BUILDARCH) $(BUILDCMD) \
36+
-o "$(OUT)/$(BINNAME)" "$(SRC)"
37+
GORACE = GOOS=$(BUILDOS) GOARCH=$(BUILDARCH) $(BUILDCMD) \
38+
-race -o "$(OUT)/$(BINNAME)-race" "$(SRC)"
3739

3840
SERVICE = $(APPNAME).service
3941

@@ -123,6 +125,12 @@ lint:
123125
lint-verbose:
124126
@$(GOLINT) run -v ./...
125127

128+
build-race: prep
129+
@$(GORACE)
130+
131+
race: build-race
132+
@$(OUT)/$(BINNAME)-race -debug
133+
126134
cover: test-cover
127135
@$(GO) tool cover -html=$(TESTCOVER) -o $(TESTCOVER).html
128136
@printf "cover: %s\n" "$$(file $(TESTCOVER).html)"

handlers/form_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,16 @@ func TestParseFormDuration(t *testing.T) {
8282
def, def, maximum},
8383
{"fraction", "/?duration=1.5h", "duration",
8484
def, 90 * time.Minute, maximum},
85+
{"no-unit", "/?duration=3333", "duration",
86+
def, def, maximum},
87+
{"bad-unit", "/duration=8h", "duration",
88+
def, def, maximum},
8589
{"large", "/?duration=9999h", "duration",
8690
def, maximum, maximum},
8791
{"xlarge", "/?duration=99999999999h", "duration",
8892
def, def, maximum}, // overflows int64
93+
{"encoded", "/?duration=%32%34%68", "duration",
94+
def, 24 * time.Hour, maximum}, // "24h" encoded
8995
}
9096
for _, tc := range tests {
9197
tc := tc

handlers/json.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package handlers
2+
3+
import (
4+
"encoding/json"
5+
"net/http"
6+
)
7+
8+
// deny serves a JSON response for disallowed requests.
9+
func deny(w http.ResponseWriter, code int, reason string, r *Request) {
10+
writeJSON(w, code, errorJSON(reason))
11+
}
12+
13+
// errorJSON returns an error string map containing the string.
14+
func errorJSON(s string) map[string]string {
15+
return map[string]string{
16+
"error": s,
17+
}
18+
}
19+
20+
// writeJSON serves a JSON response with data.
21+
func writeJSON(w http.ResponseWriter, code int, data interface{}) {
22+
w.Header().Set("Content-Type", "application/json; charset=utf-8")
23+
w.WriteHeader(code)
24+
err := json.NewEncoder(w).Encode(data)
25+
if err != nil {
26+
w.WriteHeader(http.StatusInternalServerError)
27+
_ = json.NewEncoder(w).Encode(errorJSON(err.Error()))
28+
return
29+
}
30+
}

handlers/list.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ func List(app *config.App) http.HandlerFunc {
1313
if req == nil {
1414
return
1515
}
16-
app.UpdateTimeRemaining()
1716
files := app.ListFiles()
1817
app.Log.Info("serving file list",
1918
"files", len(files), "user", req)

handlers/message.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,6 @@ func Message(app *config.App) http.HandlerFunc {
4141
if formContent != "" {
4242
message.Count++
4343
message.Data = formContent
44-
app.Log.Debug("adding message",
45-
"count", message.Count,
46-
"content", message.Data, "user", req)
4744
app.Messages[message.Count] = &message
4845
app.Log.Info("added message", "user", req)
4946
}
@@ -52,7 +49,7 @@ func Message(app *config.App) http.HandlerFunc {
5249
}
5350

5451
if r.URL.Query().Get("download") == "all" {
55-
app.Log.Debug("serving all messages",
52+
app.Log.Debug("downloading messages",
5653
"count", app.NumMessages, "user", req)
5754
app.ServeMessages(w)
5855
return

handlers/param.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package handlers
2+
3+
import "net/http"
4+
5+
// getRequestParameter returns a parameter from the request
6+
// URL or a form value.
7+
func getRequestParameter(
8+
r *http.Request, pathLen int, fieldName string) string {
9+
if pathLen > len(r.URL.Path) {
10+
return ""
11+
}
12+
p := r.URL.Path[pathLen:]
13+
if p != "" {
14+
return p
15+
}
16+
if queryValue := r.URL.Query().Get(fieldName); queryValue != "" {
17+
return queryValue
18+
}
19+
return r.FormValue(fieldName)
20+
}

handlers/param_test.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package handlers
2+
3+
import (
4+
"net/http"
5+
"net/url"
6+
"testing"
7+
)
8+
9+
// TestGetRequestParameter validates the parameter value
10+
// is read from the URL or form.
11+
func TestGetRequestParameter(t *testing.T) {
12+
tests := []struct {
13+
name string
14+
request *http.Request
15+
pathLen int
16+
field string
17+
want string
18+
}{
19+
{
20+
name: "URL parameter",
21+
request: &http.Request{
22+
URL: &url.URL{Path: "/download/id1"},
23+
},
24+
pathLen: 10,
25+
field: "",
26+
want: "id1",
27+
},
28+
{
29+
name: "Query parameter",
30+
request: &http.Request{
31+
URL: &url.URL{
32+
Path: "/download/",
33+
RawQuery: "field=id2",
34+
},
35+
},
36+
pathLen: 10,
37+
field: "field",
38+
want: "id2",
39+
},
40+
{
41+
name: "Form parameter",
42+
request: &http.Request{
43+
Body: http.NoBody,
44+
Form: url.Values{"field": {"id3"}},
45+
URL: &url.URL{Path: "/download/"},
46+
},
47+
pathLen: 10,
48+
field: "field",
49+
want: "id3",
50+
},
51+
{
52+
name: "No parameter",
53+
request: &http.Request{
54+
Body: http.NoBody,
55+
URL: &url.URL{Path: "/download/"},
56+
},
57+
pathLen: 10,
58+
field: "",
59+
want: "",
60+
},
61+
}
62+
for _, test := range tests {
63+
t.Run(test.name, func(t *testing.T) {
64+
result := getRequestParameter(test.request, test.pathLen, test.field)
65+
if result != test.want {
66+
t.Fatalf("%s: expect '%q'; got '%q'", test.name, test.want, result)
67+
}
68+
})
69+
}
70+
}

handlers/theme.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package handlers
2+
3+
import (
4+
"net/http"
5+
"slices"
6+
"time"
7+
8+
"github.com/drduh/gone/auth"
9+
"github.com/drduh/gone/util"
10+
)
11+
12+
const autoTheme = "auto"
13+
14+
// getDefaultTheme returns a default theme, based on
15+
// the current time if set to automatically theme.
16+
func getDefaultTheme(theme string) string {
17+
if theme != autoTheme {
18+
return theme
19+
}
20+
if util.IsDaytime() {
21+
return "light"
22+
}
23+
return "dark"
24+
}
25+
26+
// getTheme returns the CSS theme based on cookie preference,
27+
// setting the cookie value if none exists, or is invalid.
28+
func getTheme(w http.ResponseWriter, r *http.Request,
29+
defaultTheme, id string, t time.Duration, themes []string) string {
30+
formContent := r.FormValue(formFieldTheme)
31+
if formContent != "" {
32+
theme := formContent
33+
if !slices.Contains(themes, theme) {
34+
theme = getDefaultTheme(autoTheme)
35+
}
36+
http.SetCookie(w, auth.NewCookie(theme, id, t))
37+
return theme
38+
}
39+
return auth.GetCookie(w, r, defaultTheme, id, t)
40+
}

handlers/upload.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ func Upload(app *config.App) http.HandlerFunc {
4646

4747
var upload storage.File
4848
var uploads []storage.File
49+
var mu sync.Mutex
4950
var wg sync.WaitGroup
5051

5152
formFileContent := r.MultipartForm.File["file"]
@@ -59,6 +60,8 @@ func Upload(app *config.App) http.HandlerFunc {
5960
for _, fileHeader := range formFileContent {
6061
go func(fileHeader *multipart.FileHeader) {
6162
defer wg.Done()
63+
mu.Lock()
64+
defer mu.Unlock()
6265
file, err := fileHeader.Open()
6366
if err != nil {
6467
app.Log.Error(app.Copy, "error", err.Error(), "user", req)
@@ -78,7 +81,7 @@ func Upload(app *config.App) http.HandlerFunc {
7881
}
7982

8083
filename := storage.SanitizeName(fileHeader.Filename,
81-
app.MaxSizeName, app.FilenameExtraChars)
84+
app.FilenameExtraChars, app.MaxSizeName)
8285
f := &storage.File{
8386
Name: filename,
8487
Data: buf.Bytes(),

0 commit comments

Comments
 (0)