M config/cfg.go => config/cfg.go +2 -0
@@ 18,6 18,7 @@ type (
CheckPwdScript string
NewFolderScript string
QuotaScript string
+ CSRFKey string
NeedsKey bool
}
)
@@ 38,6 39,7 @@ func New() *Config {
CheckPwdScript: os.Getenv("CHECK_PWD_SCRIPT"),
NewFolderScript: os.Getenv("NEW_FOLDER_SCRIPT"),
QuotaScript: os.Getenv("QUOTA_SCRIPT"),
+ CSRFKey: os.Getenv("CSRF_KEY"),
NeedsKey: os.Getenv("NEEDS_KEY") != "",
}
}
M go.mod => go.mod +1 -0
@@ 3,6 3,7 @@ module ichi
go 1.16
require (
+ github.com/gorilla/csrf v1.7.1 // indirect
github.com/gorilla/mux v1.8.0
github.com/gorilla/sessions v1.2.1
github.com/lib/pq v1.10.3
M go.sum => go.sum +4 -0
@@ 1,3 1,5 @@
+github.com/gorilla/csrf v1.7.1 h1:Ir3o2c1/Uzj6FBxMlAUB6SivgVMy1ONXwYgXn+/aHPE=
+github.com/gorilla/csrf v1.7.1/go.mod h1:+a/4tCmqhG6/w4oafeAZ9pEa3/NZOWYVbD9fV0FwIQA=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
@@ 6,6 8,8 @@ github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7Fsg
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/lib/pq v1.10.3 h1:v9QZf2Sn6AmjXtQeFpdoq/eaNtYP6IN+7lcrygsIAtg=
github.com/lib/pq v1.10.3/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
golang.org/x/net v0.0.0-20210929193557-e81a3d93ecf6 h1:Z04ewVs7JhXaYkmDhBERPi41gnltfQpMWDnTnQbaCqk=
golang.org/x/net v0.0.0-20210929193557-e81a3d93ecf6/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
M web/handler/common.go => web/handler/common.go +2 -0
@@ 28,7 28,9 @@ var TplCommonMap = map[string]string{
<footer>
<p>
<a href="/"><img src="/banner.png" alt="Ichi"/></a>
+ <a href="https://status.cafe"><img src="https://status.cafe/assets/button.png" alt="Status Cafe"/></a>
<a target="_blank" href="https://melonking.net"><img alt="Visit Melonking.Net!" src="https://melonking.net/images/badges/MELON-BADGE-2.GIF"></a>
+ <a href="https://forum.agoraroad.com/"><img alt="Agora Road" src="https://forum.agoraroad.com/data/addonflare/awardsystem/icons/180.gif"></a>
<a href="/help#exchange-badge">Your badge here?</a>
</p>
<script src="https://yesterwebring.neocities.org/script-text.js"></script>
M web/handler/editor.go => web/handler/editor.go +8 -6
@@ 1,6 1,7 @@
package handler
import (
+ "github.com/gorilla/csrf"
"ichi/web/handler/form"
"io"
"net/http"
@@ 69,12 70,13 @@ func (h *Handler) handleEditor(w http.ResponseWriter, r *http.Request) {
mode = "css"
}
h.view("editor").Execute(w, map[string]interface{}{
- "Author": user,
- "Dir": dir,
- "File": file,
- "Content": content,
- "Nav": makeNav(dir),
- "Mode": mode,
+ "Author": user,
+ "Dir": dir,
+ "File": file,
+ "Content": content,
+ "Nav": makeNav(dir),
+ "Mode": mode,
+ csrf.TemplateTag: csrf.TemplateField(r),
})
return
} else {
M web/handler/files.go => web/handler/files.go +17 -12
@@ 3,6 3,7 @@ package handler
import (
"errors"
"fmt"
+ "github.com/gorilla/csrf"
"html/template"
"ichi/web/handler/form"
"io"
@@ 88,11 89,12 @@ func (h *Handler) handleFiles(w http.ResponseWriter, r *http.Request) {
serverError(w, err)
}
h.renderLayout(w, "files", map[string]interface{}{
- "Dir": dir,
- "Page": page,
- "Files": finfos,
- "Nav": makeNav(dir),
- "Quota": q,
+ "Dir": dir,
+ "Page": page,
+ "Files": finfos,
+ "Nav": makeNav(dir),
+ "Quota": q,
+ csrf.TemplateTag: csrf.TemplateField(r),
}, user)
}
@@ 148,8 150,9 @@ func (h *Handler) handleNewFile(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
h.renderLayout(w, "new-file", map[string]interface{}{
- "Dir": dir,
- "Nav": makeNav(dir),
+ "Dir": dir,
+ "Nav": makeNav(dir),
+ csrf.TemplateTag: csrf.TemplateField(r),
}, user)
return
case "POST":
@@ 197,9 200,10 @@ func (h *Handler) handleRename(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
h.renderLayout(w, "rename", map[string]interface{}{
- "Dir": dir,
- "File": file,
- "Nav": makeNav(dir),
+ "Dir": dir,
+ "File": file,
+ "Nav": makeNav(dir),
+ csrf.TemplateTag: csrf.TemplateField(r),
}, user)
return
case "POST":
@@ 230,8 234,9 @@ func (h *Handler) handleNewFolder(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
h.renderLayout(w, "new-folder", map[string]interface{}{
- "Dir": dir,
- "Nav": makeNav(dir),
+ "Dir": dir,
+ "Nav": makeNav(dir),
+ csrf.TemplateTag: csrf.TemplateField(r),
}, user)
return
case "POST":
M web/handler/handler.go => web/handler/handler.go +1 -1
@@ 59,7 59,7 @@ func New(cfg *config.Config, sess *session.Session, host string, data *storage.S
}
h.initTpl()
- // Index
+ // Routes
router.HandleFunc("/", h.showIndexView).Methods(http.MethodGet)
router.HandleFunc("/login", h.showLoginView).Methods(http.MethodGet)
router.HandleFunc("/check-login", h.checkLogin).Methods(http.MethodPost)
M web/handler/html.go => web/handler/html.go +8 -9
@@ 42,6 42,7 @@ var TplMap = map[string]string{
</head>
<body>
<form>
+ {{ .csrfField }}
<nav><a href="/"><img src="/logo.png" alt="ichi" style="width: 88px; height: 31px;"></a></nav>
<div class="file">
<input type="hidden" name="name" value="{{ .File }}"/>
@@ 80,15 81,6 @@ var TplMap = map[string]string{
}
});
})
- // function save(e) {
- // e.stopPropagation();
- // e.preventDefault();
- // const form = new FormData(document.querySelector('form'));
- // fetch('/editor?dir={{ .Dir }}', {
- // method: 'POST',
- // body: form
- // });
- // }
</script>
</body>
</html>`,
@@ 180,6 172,7 @@ var TplMap = map[string]string{
<td style="padding: 2px 8px; text-align: right;"><a href="/rename?dir={{ $.Dir }}&file={{ .Name }}">rename</a></li></td>
<td style="text-align: right;">
<form action="/delete?dir={{ $.Dir }}&file={{ .Name }}" method="post" class="auth-form">
+ {{ $.csrfField }}
<input style="padding: 0 4px;" class="button" type="submit" onclick="return confirm('delete?');" value="delete">
</form>
</td>
@@ 193,6 186,7 @@ var TplMap = map[string]string{
action="/upload?dir={{ .Dir }}"
method="post"
>
+ {{ .csrfField }}
<input type="file" name="file" />
<input type="submit" value="upload" />
</form>
@@ 377,6 371,7 @@ var TplMap = map[string]string{
<p>{{ .form.Error }}</p>
{{ end }}
<form action="/check-login" method="post" class="auth-form">
+ {{ .csrfField }}
<div class="field">
<label for="name">Username</label>
<input type="text" id="name" name="name" autocomplete="off" required autofocus/>
@@ 395,6 390,7 @@ var TplMap = map[string]string{
<article>
<nav>{{ .Nav }}</nav>
<form action="/new-file?dir={{ .Dir }}" method="post" class="auth-form">
+ {{ .csrfField }}
<div class="field">
<input placeholder="file name" type="text" id="name" name="name" autocomplete="off" required/>
<input type="hidden" name="content" value=""/>
@@ 408,6 404,7 @@ var TplMap = map[string]string{
<article>
<nav>{{ .Nav }}</nav>
<form action="/new-folder?dir={{ .Dir }}" method="post" class="auth-form">
+ {{ .csrfField }}
<div class="field">
<input placeholder="folder name" type="text" id="name" name="name" autocomplete="off" required/>
</div>
@@ 433,6 430,7 @@ var TplMap = map[string]string{
<p>{{ .form.Error }}</p>
{{ end }}
<form action="/register" method="post" class="auth-form">
+ {{ .csrfField }}
<div class="field">
<label for="name">Username</label>
<input type="text" id="name" name="name" autocomplete="off" required autofocus/>
@@ 464,6 462,7 @@ var TplMap = map[string]string{
<article>
<nav>{{ .Nav }}/{{ .File }}</nav>
<form action="/rename?dir={{ .Dir }}&file={{ .File }}" method="post" class="auth-form">
+ {{ .csrfField }}
<div class="field">
<input placeholder="file name" type="text" id="name" name="name" autocomplete="off" required/>
<input type="hidden" name="content" value=""/>
M web/handler/html/common/layout.html => web/handler/html/common/layout.html +2 -0
@@ 23,7 23,9 @@
<footer>
<p>
<a href="/"><img src="/banner.png" alt="Ichi"/></a>
+ <a href="https://status.cafe"><img src="https://status.cafe/assets/button.png" alt="Status Cafe"/></a>
<a target="_blank" href="https://melonking.net"><img alt="Visit Melonking.Net!" src="https://melonking.net/images/badges/MELON-BADGE-2.GIF"></a>
+ <a href="https://forum.agoraroad.com/"><img alt="Agora Road" src="https://forum.agoraroad.com/data/addonflare/awardsystem/icons/180.gif"></a>
<a href="/help#exchange-badge">Your badge here?</a>
</p>
<script src="https://yesterwebring.neocities.org/script-text.js"></script>
M web/handler/html/editor.html => web/handler/html/editor.html +1 -9
@@ 37,6 37,7 @@
</head>
<body>
<form>
+ {{ .csrfField }}
<nav><a href="/"><img src="/logo.png" alt="ichi" style="width: 88px; height: 31px;"></a></nav>
<div class="file">
<input type="hidden" name="name" value="{{ .File }}"/>
@@ 75,15 76,6 @@
}
});
})
- // function save(e) {
- // e.stopPropagation();
- // e.preventDefault();
- // const form = new FormData(document.querySelector('form'));
- // fetch('/editor?dir={{ .Dir }}', {
- // method: 'POST',
- // body: form
- // });
- // }
</script>
</body>
</html>=
\ No newline at end of file
M web/handler/html/files.html => web/handler/html/files.html +2 -0
@@ 39,6 39,7 @@
<td style="padding: 2px 8px; text-align: right;"><a href="/rename?dir={{ $.Dir }}&file={{ .Name }}">rename</a></li></td>
<td style="text-align: right;">
<form action="/delete?dir={{ $.Dir }}&file={{ .Name }}" method="post" class="auth-form">
+ {{ $.csrfField }}
<input style="padding: 0 4px;" class="button" type="submit" onclick="return confirm('delete?');" value="delete">
</form>
</td>
@@ 52,6 53,7 @@
action="/upload?dir={{ .Dir }}"
method="post"
>
+ {{ .csrfField }}
<input type="file" name="file" />
<input type="submit" value="upload" />
</form>
M web/handler/html/login.html => web/handler/html/login.html +1 -0
@@ 5,6 5,7 @@
<p>{{ .form.Error }}</p>
{{ end }}
<form action="/check-login" method="post" class="auth-form">
+ {{ .csrfField }}
<div class="field">
<label for="name">Username</label>
<input type="text" id="name" name="name" autocomplete="off" required autofocus/>
M web/handler/html/new-file.html => web/handler/html/new-file.html +1 -0
@@ 3,6 3,7 @@
<article>
<nav>{{ .Nav }}</nav>
<form action="/new-file?dir={{ .Dir }}" method="post" class="auth-form">
+ {{ .csrfField }}
<div class="field">
<input placeholder="file name" type="text" id="name" name="name" autocomplete="off" required/>
<input type="hidden" name="content" value=""/>
M web/handler/html/new-folder.html => web/handler/html/new-folder.html +1 -0
@@ 3,6 3,7 @@
<article>
<nav>{{ .Nav }}</nav>
<form action="/new-folder?dir={{ .Dir }}" method="post" class="auth-form">
+ {{ .csrfField }}
<div class="field">
<input placeholder="folder name" type="text" id="name" name="name" autocomplete="off" required/>
</div>
M web/handler/html/register.html => web/handler/html/register.html +1 -0
@@ 5,6 5,7 @@
<p>{{ .form.Error }}</p>
{{ end }}
<form action="/register" method="post" class="auth-form">
+ {{ .csrfField }}
<div class="field">
<label for="name">Username</label>
<input type="text" id="name" name="name" autocomplete="off" required autofocus/>
M web/handler/html/rename.html => web/handler/html/rename.html +1 -0
@@ 3,6 3,7 @@
<article>
<nav>{{ .Nav }}/{{ .File }}</nav>
<form action="/rename?dir={{ .Dir }}&file={{ .File }}" method="post" class="auth-form">
+ {{ .csrfField }}
<div class="field">
<input placeholder="file name" type="text" id="name" name="name" autocomplete="off" required/>
<input type="hidden" name="content" value=""/>
M web/handler/login_check.go => web/handler/login_check.go +3 -1
@@ 1,6 1,7 @@
package handler
import (
+ "github.com/gorilla/csrf"
"ichi/web/handler/form"
"net/http"
"os/exec"
@@ 12,7 13,8 @@ func (h *Handler) checkLogin(w http.ResponseWriter, r *http.Request) {
if err != nil {
f.Error = "incorrect password"
h.renderLayout(w, "login", map[string]interface{}{
- "form": f,
+ "form": f,
+ csrf.TemplateTag: csrf.TemplateField(r),
}, "")
return
}
M web/handler/login_show.go => web/handler/login_show.go +3 -1
@@ 1,6 1,7 @@
package handler
import (
+ "github.com/gorilla/csrf"
"ichi/web/handler/form"
"net/http"
)
@@ 8,6 9,7 @@ import (
func (h *Handler) showLoginView(w http.ResponseWriter, r *http.Request) {
loginForm := form.LoginForm{}
h.renderLayout(w, "login", map[string]interface{}{
- "form": loginForm,
+ "form": loginForm,
+ csrf.TemplateTag: csrf.TemplateField(r),
}, "")
}
M web/handler/register.go => web/handler/register.go +2 -1
@@ 5,6 5,7 @@ import (
"encoding/json"
"errors"
"fmt"
+ "github.com/gorilla/csrf"
"html/template"
"ichi/web/handler/form"
"io/ioutil"
@@ 31,7 32,7 @@ func (h *Handler) handleRegister(w http.ResponseWriter, r *http.Request) {
func (h *Handler) showRegisterView(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
- h.renderLayout(w, "register", map[string]interface{}{"needsKey": h.cfg.NeedsKey}, "")
+ h.renderLayout(w, "register", map[string]interface{}{"needsKey": h.cfg.NeedsKey, csrf.TemplateTag: csrf.TemplateField(r)}, "")
}
}
M web/web.go => web/web.go +4 -2
@@ 2,6 2,7 @@ package web
import (
"fmt"
+ "github.com/gorilla/csrf"
"ichi/config"
"ichi/storage"
"ichi/web/handler"
@@ 31,11 32,12 @@ func Serve(data *storage.Storage, cfg *config.Config) error {
if err != nil {
log.Fatal(err)
}
+ CSRF := csrf.Protect([]byte(cfg.CSRFKey), csrf.MaxAge(0))
switch cfg.Env {
case "PROD":
go func() {
fmt.Printf("Starting HTTP server on :443\n")
- err := http.ListenAndServeTLS(":443", cfg.CertFile, cfg.KeyFile, s)
+ err := http.ListenAndServeTLS(":443", cfg.CertFile, cfg.KeyFile, CSRF(s))
if err != nil {
log.Fatalf("httpsSrv.ListendAndServeTLS() failed with %s", err)
}
@@ 45,7 47,7 @@ func Serve(data *storage.Storage, cfg *config.Config) error {
break
default:
fmt.Printf("Starting HTTP server on %s\n", host)
- err = http.ListenAndServe(host, s)
+ err = http.ListenAndServe(host, CSRF(s))
}
return err
}