v0.23.0 — CSRF protection on all browser-facing POST endpoints
Controller: - internal/web/csrf.go (new): CsrfProtect middleware, csrfToken/csrfField helpers - auth.go: per-session CSRF token (csrfToken field, csrfTokenForSession method) - server.go: executeTemplate wrapper auto-injects CSRFField+CSRFToken - main.go: wire CsrfProtect on all routes; bump to v0.23.0 - handlers.go, storage_handlers.go, handler_restore.go: executeTemplate - All templates: CSRFField in forms, meta csrf-token, csrfHeaders() JS helper, fetch calls updated; sendBeacon→fetch+keepalive in storage_attach.html Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -131,7 +131,7 @@ function checkUpdate() {
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'Ellenőrzés...';
|
||||
msg.style.display = 'none';
|
||||
fetch('/api/selfupdate/check', {method:'POST'})
|
||||
fetch('/api/selfupdate/check', {method:'POST', headers: csrfHeaders()})
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(data) {
|
||||
if (data.ok) {
|
||||
@@ -161,7 +161,7 @@ function triggerUpdate() {
|
||||
if (checkBtn) checkBtn.disabled = true;
|
||||
msg.textContent = 'Frissítés folyamatban...';
|
||||
msg.style.display = 'inline';
|
||||
fetch('/api/selfupdate/update', {method:'POST'})
|
||||
fetch('/api/selfupdate/update', {method:'POST', headers: csrfHeaders()})
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(data) {
|
||||
if (data.ok) {
|
||||
@@ -248,6 +248,7 @@ function pollUntilBack() {
|
||||
<div class="storage-path-actions">
|
||||
<form method="POST" action="/settings/storage/remove" style="display:inline"
|
||||
onsubmit="return confirm('Biztosan eltávolítja a(z) {{.Label}} ({{.Path}}) meghajtót a rendszerből?\n\nA meghajtó adatai NEM törlődnek.')">
|
||||
{{$.CSRFField}}
|
||||
<input type="hidden" name="storage_path" value="{{.Path}}">
|
||||
<button type="submit" class="btn btn-xs btn-outline">Eltávolítás a rendszerből</button>
|
||||
</form>
|
||||
@@ -300,18 +301,21 @@ function pollUntilBack() {
|
||||
<div class="storage-path-actions">
|
||||
{{if not .IsDefault}}
|
||||
<form method="POST" action="/settings/storage/default" style="display:inline">
|
||||
{{$.CSRFField}}
|
||||
<input type="hidden" name="storage_path" value="{{.Path}}">
|
||||
<button type="submit" class="btn btn-xs btn-outline">Legyen alapértelmezett</button>
|
||||
</form>
|
||||
{{end}}
|
||||
{{if .Schedulable}}
|
||||
<form method="POST" action="/settings/storage/schedulable" style="display:inline">
|
||||
{{$.CSRFField}}
|
||||
<input type="hidden" name="storage_path" value="{{.Path}}">
|
||||
<input type="hidden" name="schedulable" value="false">
|
||||
<button type="submit" class="btn btn-xs btn-outline">Letiltás</button>
|
||||
</form>
|
||||
{{else}}
|
||||
<form method="POST" action="/settings/storage/schedulable" style="display:inline">
|
||||
{{$.CSRFField}}
|
||||
<input type="hidden" name="storage_path" value="{{.Path}}">
|
||||
<input type="hidden" name="schedulable" value="true">
|
||||
<button type="submit" class="btn btn-xs btn-outline">Engedélyezés</button>
|
||||
@@ -323,6 +327,7 @@ function pollUntilBack() {
|
||||
{{if and (not .IsDefault) (eq .AppCount 0)}}
|
||||
<form method="POST" action="/settings/storage/remove" style="display:inline"
|
||||
onsubmit="return confirm('Biztosan eltávolítja a(z) {{.Path}} adattárolót?')">
|
||||
{{$.CSRFField}}
|
||||
<input type="hidden" name="storage_path" value="{{.Path}}">
|
||||
<button type="submit" class="btn btn-xs btn-danger-outline">Eltávolítás</button>
|
||||
</form>
|
||||
@@ -349,6 +354,7 @@ function pollUntilBack() {
|
||||
<details class="storage-add-details">
|
||||
<summary class="btn btn-sm btn-outline" style="margin-top:.75rem;cursor:pointer">Már csatlakoztatott tárhely hozzáadása kézzel</summary>
|
||||
<form method="POST" action="/settings/storage/add" class="storage-add-form">
|
||||
{{.CSRFField}}
|
||||
<div class="form-group">
|
||||
<label for="storage_path">Elérési út</label>
|
||||
<input type="text" id="storage_path" name="storage_path" class="form-control"
|
||||
@@ -375,6 +381,7 @@ function pollUntilBack() {
|
||||
{{if .AuthEnabled}}
|
||||
{{if .PasswordError}}<div class="alert alert-error">{{.PasswordError}}</div>{{end}}
|
||||
<form method="POST" action="/settings/password">
|
||||
{{.CSRFField}}
|
||||
<div class="form-group">
|
||||
<label for="current_password">Jelenlegi jelszó</label>
|
||||
<input type="password" id="current_password" name="current_password" required
|
||||
@@ -406,6 +413,7 @@ function pollUntilBack() {
|
||||
{{if .NotificationSuccess}}<div class="alert alert-info">{{.NotificationSuccess}}</div>{{end}}
|
||||
{{if .NotificationError}}<div class="alert alert-error">{{.NotificationError}}</div>{{end}}
|
||||
<form method="POST" action="/settings/notifications">
|
||||
{{.CSRFField}}
|
||||
<div class="form-group">
|
||||
<label for="notification_email">E-mail cím</label>
|
||||
<input type="email" id="notification_email" name="notification_email"
|
||||
@@ -531,7 +539,9 @@ function pollUntilBack() {
|
||||
function editStorageLabel(path, currentLabel) {
|
||||
var wrap = document.getElementById('label-wrap-' + path);
|
||||
if (!wrap) return;
|
||||
var csrfTok = (document.querySelector('meta[name="csrf-token"]') || {}).content || '';
|
||||
wrap.innerHTML = '<form method="POST" action="/settings/storage/label" style="display:inline-flex;gap:.5rem;align-items:center">' +
|
||||
'<input type="hidden" name="_csrf" value="' + csrfTok + '">' +
|
||||
'<input type="hidden" name="storage_path" value="' + path + '">' +
|
||||
'<input type="text" name="storage_label" class="form-control" value="' + currentLabel.replace(/"/g, '"') + '" style="width:200px;padding:.3rem .5rem;font-size:.9rem" maxlength="50">' +
|
||||
'<button type="submit" class="btn btn-xs btn-primary">OK</button>' +
|
||||
@@ -546,7 +556,7 @@ function storageDisconnect(path, label, appCount) {
|
||||
if (!confirm(msg)) return;
|
||||
fetch('/api/storage/disconnect', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()),
|
||||
body: JSON.stringify({path: path})
|
||||
}).then(function(r) { return r.json(); }).then(function(data) {
|
||||
if (data.ok) {
|
||||
@@ -562,7 +572,7 @@ function storageReconnect(path) {
|
||||
if (actionsDiv) actionsDiv.innerHTML = '<span class="form-hint">Csatlakoztatás...</span>';
|
||||
fetch('/api/storage/reconnect', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()),
|
||||
body: JSON.stringify({path: path})
|
||||
}).then(function(r) { return r.json(); }).then(function(data) {
|
||||
if (data.ok) {
|
||||
@@ -579,7 +589,7 @@ function storageReconnect(path) {
|
||||
function storageRestartApps(path) {
|
||||
fetch('/api/storage/restart-apps', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()),
|
||||
body: JSON.stringify({path: path})
|
||||
}).then(function(r) { return r.json(); }).then(function(data) {
|
||||
if (data.ok) {
|
||||
|
||||
Reference in New Issue
Block a user