Add CSRF protection to state-changing POST endpoints
Add gorilla/csrf middleware to protect all HTML-serving routes against
cross-site request forgery attacks. The webhook endpoint is excluded
since it uses secret-based authentication.
Changes:
- Add gorilla/csrf v1.7.3 dependency
- Add CSRF() middleware method using session secret as key
- Apply CSRF middleware to all HTML route groups in routes.go
- Pass CSRF token to all templates via addGlobals helper
- Add {{ .CSRFField }} / {{ $.CSRFField }} hidden inputs to all forms
Closes #11
This commit is contained in:
@@ -35,6 +35,7 @@
|
||||
<div class="flex gap-3">
|
||||
<a href="/apps/{{.App.ID}}/edit" class="btn-secondary">Edit</a>
|
||||
<form method="POST" action="/apps/{{.App.ID}}/deploy" class="inline" @submit="submitDeploy()">
|
||||
{{ .CSRFField }}
|
||||
<button type="submit" class="btn-success" x-bind:disabled="deploying" x-bind:class="{ 'opacity-50 cursor-not-allowed': deploying }">
|
||||
<span x-text="deploying ? 'Deploying...' : 'Deploy Now'"></span>
|
||||
</button>
|
||||
@@ -106,6 +107,7 @@
|
||||
<td class="font-mono text-gray-500">{{.Value}}</td>
|
||||
<td class="text-right">
|
||||
<form method="POST" action="/apps/{{$.App.ID}}/env/{{.ID}}/delete" class="inline" x-data="confirmAction('Delete this environment variable?')" @submit="confirm($event)">
|
||||
{{ .CSRFField }}
|
||||
<button type="submit" class="text-error-500 hover:text-error-700 text-sm">Delete</button>
|
||||
</form>
|
||||
</td>
|
||||
@@ -116,6 +118,7 @@
|
||||
</div>
|
||||
{{end}}
|
||||
<form method="POST" action="/apps/{{.App.ID}}/env" class="flex flex-col sm:flex-row gap-2">
|
||||
{{ .CSRFField }}
|
||||
<input type="text" name="key" placeholder="KEY" required class="input flex-1 font-mono text-sm">
|
||||
<input type="text" name="value" placeholder="value" required class="input flex-1 font-mono text-sm">
|
||||
<button type="submit" class="btn-primary">Add</button>
|
||||
@@ -149,6 +152,7 @@
|
||||
<td class="font-mono text-gray-500">{{.Value}}</td>
|
||||
<td class="text-right">
|
||||
<form method="POST" action="/apps/{{$.App.ID}}/labels/{{.ID}}/delete" class="inline" x-data="confirmAction('Delete this label?')" @submit="confirm($event)">
|
||||
{{ .CSRFField }}
|
||||
<button type="submit" class="text-error-500 hover:text-error-700 text-sm">Delete</button>
|
||||
</form>
|
||||
</td>
|
||||
@@ -158,6 +162,7 @@
|
||||
</table>
|
||||
</div>
|
||||
<form method="POST" action="/apps/{{.App.ID}}/labels" class="flex flex-col sm:flex-row gap-2">
|
||||
{{ .CSRFField }}
|
||||
<input type="text" name="key" placeholder="label.key" required class="input flex-1 font-mono text-sm">
|
||||
<input type="text" name="value" placeholder="value" required class="input flex-1 font-mono text-sm">
|
||||
<button type="submit" class="btn-primary">Add</button>
|
||||
@@ -192,6 +197,7 @@
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<form method="POST" action="/apps/{{$.App.ID}}/volumes/{{.ID}}/delete" class="inline" x-data="confirmAction('Delete this volume mount?')" @submit="confirm($event)">
|
||||
{{ .CSRFField }}
|
||||
<button type="submit" class="text-error-500 hover:text-error-700 text-sm">Delete</button>
|
||||
</form>
|
||||
</td>
|
||||
@@ -202,6 +208,7 @@
|
||||
</div>
|
||||
{{end}}
|
||||
<form method="POST" action="/apps/{{.App.ID}}/volumes" class="flex flex-col sm:flex-row gap-2 items-end">
|
||||
{{ .CSRFField }}
|
||||
<div class="flex-1 w-full">
|
||||
<input type="text" name="host_path" placeholder="/host/path" required class="input font-mono text-sm">
|
||||
</div>
|
||||
@@ -244,6 +251,7 @@
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<form method="POST" action="/apps/{{$.App.ID}}/ports/{{.ID}}/delete" class="inline" x-data="confirmAction('Delete this port mapping?')" @submit="confirm($event)">
|
||||
{{ .CSRFField }}
|
||||
<button type="submit" class="text-error-500 hover:text-error-700 text-sm">Delete</button>
|
||||
</form>
|
||||
</td>
|
||||
@@ -254,6 +262,7 @@
|
||||
</div>
|
||||
{{end}}
|
||||
<form method="POST" action="/apps/{{.App.ID}}/ports" class="flex flex-col sm:flex-row gap-2 items-end">
|
||||
{{ .CSRFField }}
|
||||
<div class="flex-1 w-full">
|
||||
<label class="block text-xs text-gray-500 mb-1">Host (external)</label>
|
||||
<input type="text" name="host_port" placeholder="8080" required pattern="[0-9]+" class="input font-mono text-sm">
|
||||
@@ -339,6 +348,7 @@
|
||||
<h2 class="text-lg font-medium text-error-700 mb-4">Danger Zone</h2>
|
||||
<p class="text-error-600 text-sm mb-4">Deleting this app will remove all configuration and deployment history. This action cannot be undone.</p>
|
||||
<form method="POST" action="/apps/{{.App.ID}}/delete" x-data="confirmAction('Are you sure you want to delete this app? This action cannot be undone.')" @submit="confirm($event)">
|
||||
{{ .CSRFField }}
|
||||
<button type="submit" class="btn-danger">Delete App</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user