replace ?r=/redirect_to with something else.

can't shake the fakey feeling. net/http expects one handler max. the way we currently detect redirect param is not one handler.
      we should refactor the code out to be called from different handlers
      also, wtf is /settings/novelFontType?noredirect=t&r=  (redirect or no redirect?)
      suggestion: Use Referer. the header exists for a reason. might need to turn off CSP for that.
This commit is contained in:
iacore
2024-08-27 16:21:11 +00:00
committed by VnPower
parent 5ec33ae18b
commit c9f3884f92
10 changed files with 34 additions and 74 deletions
+3 -3
View File
@@ -36,11 +36,11 @@
</div>
<div class="artwork-actions">
{{ if isset(.BookmarkData) && LoggedIn }}
<a href="/self/deleteBookmark/{{ .BookmarkID }}?r={{ OriginalURL }}">
<a href="/self/deleteBookmark/{{ .BookmarkID }}">
<img src="/img/heart-red.png" alt="Bookmarked" />
</a>
{{ else }}
<a href="/self/addBookmark/{{ .ID }}?r={{ OriginalURL }}">
<a href="/self/addBookmark/{{ .ID }}">
<img src="/img/heart-outline.png" alt="Bookmark" />
</a>
{{ end }}
@@ -49,7 +49,7 @@
<img src="/img/like-blue.png" alt="Liked" />
{{ else }}
<a href="/self/like/{{ .ID }}?r={{ OriginalURL }}">
<a href="/self/like/{{ .ID }}">
<img src="/img/like-outline.png" alt="Like" />
</a>
{{ end }}
+2 -3
View File
@@ -64,7 +64,7 @@
<div class="novel-settings">
<div class="novel-settings-wrapper">
<span class="dropdown">
{{ url := "/settings/novelFontType?noredirect=t&r=" + OriginalURL + "#content" }}
{{ url := "/settings/novelFontType" }}
<input type="checkbox" class="dropdown-toggler" id="font-type-toggler" />
<label for="font-type-toggler" class="dropdown-toggler-label">
<img src="/img/font-family.png" alt="Font family" />
@@ -83,7 +83,7 @@
</span>
<img src="/img/font-size.png" alt="Font size" />
<span class="dropdown">
{{ url := "/settings/novelViewMode?r=" + OriginalURL + "#content" }}
{{ url := "/settings/novelViewMode" }}
<input type="checkbox" class="dropdown-toggler" id="view-mode-toggler" />
<label for="view-mode-toggler" class="dropdown-toggler-label">
<img src="/img/orientation.png" alt="Orientation" />
@@ -100,7 +100,6 @@
<input id="vm-d" type="radio" name="view-mode" value="d" />
<label for="vm-d">Don't force</label>
<br />
<input type="hidden" name="noredirect" value="t" />
<input type="submit" value="Submit!" />
</form>
</div>
+5 -22
View File
@@ -1,24 +1,5 @@
# Migrating from gofiber to net/http -- the plan
- Config [already decoupled]
- Templating [decoupled, waiting for integration]
- Router
features
- /users/:id/:category? (optional path segment)
- /i.pximg.net/* (wildcard)
- Middleware
- Logging
- Rate limit (optional, could be loosely-coupled)
- Caching (optional, could be loosely-coupled)
## Problem
net/http handlers don't return errors. We have to make our own ServeMux that allows functions to return `error`, possibly.
net/http expects handlers to panic on error, while we don't panic. We need to log the errors anyway.
Idea: we create a compat layer of {w, r} that has the same API as *fiber.Ctx.
## Tips
- To access `/:abc`, use `r.PathValue("abc")`.
@@ -28,6 +9,8 @@ Idea: we create a compat layer of {w, r} that has the same API as *fiber.Ctx.
## todo
- correct redirect status codes. currently i put in whatever.
- test ?r=/redirect_to
- limiter
- caching
- 303 StatusSeeOther: set method to GET
- 307 Temporary: method and body not changed
- 308 Permanent: method and body not changed
- add limiter (maybe it should be in nginx)
- add caching (maybe it should be in nginx)
+4 -11
View File
@@ -72,17 +72,11 @@ func main() {
setGlobalHeaders(w, r)
if r.URL.Path != "/" && strings.HasSuffix(r.URL.Path, "/") {
// strip trailing / to make router behave
url := r.URL
url.Path, _ = strings.CutSuffix(url.Path, "/")
http.Redirect(w, r, url.String(), http.StatusFound)
http.Redirect(w, r, url.String(), http.StatusPermanentRedirect)
} else {
// redirect any request with ?r=url
redirect_to := r.URL.Query().Get("r")
if redirect_to != "" {
// could this be unsafe since this redirects to any website?
http.Redirect(w, r, redirect_to, http.StatusTemporaryRedirect)
}
// all the routes are listed here
router.ServeHTTP(w, r)
}
@@ -91,7 +85,7 @@ func main() {
CatchError(func(w http.ResponseWriter, r utils.CompatRequest) error {
err := GetUserContext(r.Request).err
if err != nil { // error handler
log.Println(err)
log.Println("Within handler: ", err)
code := http.StatusInternalServerError
w.WriteHeader(code)
// Send custom error page
@@ -168,7 +162,7 @@ func setGlobalHeaders(w http.ResponseWriter, r *http.Request) {
header.Add("X-Frame-Options", "DENY")
// use this if need iframe: `X-Frame-Options: SAMEORIGIN`
header.Add("X-Content-Type-Options", "nosniff")
header.Add("Referrer-Policy", "no-referrer")
header.Add("Referrer-Policy", "same-origin") // needed for settings redirect
header.Add("Strict-Transport-Security", "max-age=31536000; includeSubDomains; preload")
header.Add("Content-Security-Policy", fmt.Sprintf("base-uri 'self'; default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' %s; media-src 'self' %s; connect-src 'self'; form-action 'self'; frame-ancestors 'none';", session.GetImageProxyOrigin(r), session.GetImageProxyOrigin(r)))
// use this if need iframe: `frame-ancestors 'self'`
@@ -213,7 +207,6 @@ func defineRoutes() *mux.Router {
// Settings group
router.HandleFunc("/settings", CatchError(routes.SettingsPage)).Methods("GET")
router.HandleFunc("/settings/{type}", CatchError(routes.SettingsPost)).Methods("POST")
router.HandleFunc("/settings/{type}/{noredirect}?", CatchError(routes.SettingsPost)).Methods("POST")
// Personal group
router.HandleFunc("/self", CatchError(routes.LoginUserPage)).Methods("GET")
+6 -3
View File
@@ -82,7 +82,8 @@ func AddBookmarkRoute(w http.ResponseWriter, r CompatRequest) error {
return err
}
return SendString(w, "Success")
RedirectToWhenceYouCame(w, r)
return nil
}
func DeleteBookmarkRoute(w http.ResponseWriter, r CompatRequest) error {
@@ -105,7 +106,8 @@ func DeleteBookmarkRoute(w http.ResponseWriter, r CompatRequest) error {
return err
}
return SendString(w, "Success")
RedirectToWhenceYouCame(w, r)
return nil
}
func LikeRoute(w http.ResponseWriter, r CompatRequest) error {
@@ -127,5 +129,6 @@ func LikeRoute(w http.ResponseWriter, r CompatRequest) error {
return err
}
return SendString(w, "Success")
RedirectToWhenceYouCame(w, r)
return nil
}
-2
View File
@@ -58,7 +58,6 @@ func GetTemplatingVariables(r CompatRequest) jet.VarMap {
// Pass in values that we want to be available to all pages here
token := session.GetPixivToken(r.Request)
baseURL := r.BaseURL()
originalURL := r.OriginalURL()
pageURL := r.PageURL()
cookies := map[string]string{}
@@ -69,7 +68,6 @@ func GetTemplatingVariables(r CompatRequest) jet.VarMap {
return jet.VarMap{}.
Set("BaseURL", baseURL).
Set("OriginalURL", originalURL).
Set("PageURL", pageURL).
Set("LoggedIn", token != "").
Set("Queries", r.URL.Query().Encode()).
-1
View File
@@ -71,7 +71,6 @@ func manualTest[T any](t *testing.T, data T) {
for k, v := range map[string]any{
"BaseURL": fakeData[string](),
"OriginalURL": fakeData[string](),
"PageURL": fakeData[string](),
"LoggedIn": fakeData[bool](),
"Queries": fakeData[map[string]string](),
+9 -6
View File
@@ -160,7 +160,6 @@ func SettingsPage(w http.ResponseWriter, r CompatRequest) error {
func SettingsPost(w http.ResponseWriter, r CompatRequest) error {
t := r.Params("type")
noredirect := r.FormValue("noredirect") == ""
var err error
switch t {
@@ -192,10 +191,14 @@ func SettingsPost(w http.ResponseWriter, r CompatRequest) error {
return err
}
if !noredirect {
return nil
}
http.Redirect(w, r.Request, "/settings", http.StatusSeeOther)
RedirectToWhenceYouCame(w, r)
return nil
}
func RedirectToWhenceYouCame(w http.ResponseWriter, r CompatRequest) {
if strings.HasPrefix(r.Referer(), r.BaseURL()) {
http.Redirect(w, r.Request, r.Referer(), http.StatusSeeOther)
} else {
w.WriteHeader(200)
}
}
-11
View File
@@ -20,17 +20,6 @@ func (r CompatRequest) BaseURL() string {
Host: r.URL.Host,
}).String()
}
func (r CompatRequest) OriginalURL() string {
return (&url.URL{
Path: r.URL.Path,
RawPath: r.URL.RawPath,
OmitHost: r.URL.OmitHost,
ForceQuery: r.URL.ForceQuery,
RawQuery: r.URL.RawQuery,
Fragment: r.URL.Fragment,
RawFragment: r.URL.RawFragment,
}).String()
}
func (r CompatRequest) PageURL() string {
return r.URL.String()
}
+5 -12
View File
@@ -11,14 +11,11 @@ type PartialURL struct {
func unfinishedQuery(url PartialURL, key string) string {
result := fmt.Sprintf("/%s", url.Path)
first_query_pair := true
query_param_exists := false
for k, v := range url.Query {
k = lowercaseFirstChar(k)
if k == key {
// Reserve this
query_param_exists = true
continue
}
@@ -37,17 +34,13 @@ func unfinishedQuery(url PartialURL, key string) string {
}
// This is to move the matched query to the end of the URL
if query_param_exists {
var t string
if first_query_pair {
t = "?"
} else {
t = "&"
}
result += fmt.Sprintf("%s%s=", t, key)
var t string
if first_query_pair {
t = "?"
} else {
// todo: what now? if it doesn't exist, it's a no-op? that doesn't make sense given how navigation works.
t = "&"
}
result += fmt.Sprintf("%s%s=", t, key)
return result
}