feat(security): rate-limit /api/flows/start, CSRF double-submit cookie, CSP headers
- Rate-limit /api/flows/start to 10 req/min per user (in-memory, matches login pattern) - Add middleware.ts: validates x-csrf-token header against csrf_token cookie on all API POST requests; issues the cookie on GET if missing; sets CSP, X-Frame-Options, X-Content-Type-Options, and Referrer-Policy on all responses - Add lib/csrf.ts: client-side apiFetch() wrapper that injects the CSRF header - Update all client components (login, useFlowRun, docker, caddy, git, systemd) to use apiFetch() for POST requests - Cookie config in login route already correct (NODE_ENV check, httpOnly, sameSite=strict) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
1e31e3b584
commit
aa1fd41bec
11 changed files with 108 additions and 8 deletions
|
|
@ -2,9 +2,10 @@
|
|||
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { parseSystemctlStatus, type UnitStatus, type ActiveState } from '@/lib/parse-systemd'
|
||||
import { apiFetch } from '@/lib/csrf'
|
||||
|
||||
async function fetchOutput(commandKey: string, args: string[]): Promise<string> {
|
||||
const res = await fetch('/api/agent/exec', {
|
||||
const res = await apiFetch('/api/agent/exec', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ command_key: commandKey, args }),
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { parseSystemctlStatus, type UnitStatus, type ActiveState } from '@/lib/p
|
|||
import { useFlowRun } from '@/hooks/useFlowRun'
|
||||
import ConfirmDialog from '@/components/ConfirmDialog'
|
||||
import StreamingTerminal from '@/components/StreamingTerminal'
|
||||
import { apiFetch } from '@/lib/csrf'
|
||||
|
||||
interface UnitEntry {
|
||||
unit: string
|
||||
|
|
@ -14,7 +15,7 @@ interface UnitEntry {
|
|||
}
|
||||
|
||||
async function fetchUnitStatus(unit: string): Promise<UnitStatus> {
|
||||
const res = await fetch('/api/agent/exec', {
|
||||
const res = await apiFetch('/api/agent/exec', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ command_key: 'systemctl_status', args: [unit] }),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue