Files
FabledSteward/steward/templates/monitors/rows.html
T
bvandeusenandClaude Opus 5.5 96126b1c83
CI / lint (push) Successful in 2s
CI / integration (push) Successful in 49s
CI / unit (push) Successful in 1m3s
CI / publish (push) Successful in 1m18s
feat(auth): forged browser requests are refused (#5031)
#5031 "Forged-request protection: SameSite cookie, per-session CSRF token
on every non-GET, HTMX header". A page on another site could make a
logged-in browser POST to Steward with the session cookie attached.

- steward/auth/csrf.py: a per-session token, checked in one before_request
  for every POST/PUT/PATCH/DELETE (header X-CSRF-Token or form field
  csrf_token, constant-time compare). The bearer-authenticated host-agent
  ingest is exempt; /mcp never reaches Quart.
- Every POST form renders {{ csrf_field() }}; base.html carries the token
  in a meta tag and adds it to every HTMX request; the dashboard's fetch()
  calls send it.
- The session cookie is SameSite=Lax; login issues a fresh token.
- PUBLIC_BASE_URL is now loaded from Settings at startup (it was only set
  when Settings → General was saved).
- tests/test_csrf.py: refusal, acceptance, rotation, the ingest exemption,
  and a guard that every POST form in the templates carries the field.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 10:44:19 -04:00

75 lines
3.4 KiB
HTML

{# HTMX fragment — monitor rows (list page). #}
{% if monitor_data %}
{% for d in monitor_data %}
{% set m = d.monitor %}
{% set latest = d.latest %}
<div class="ping-row" {% if not m.enabled %}style="opacity:0.5;"{% endif %}>
<div class="ping-meta" style="min-width:160px;max-width:240px;">
<span class="badge" style="font-size:0.62rem;vertical-align:middle;">{{ m.type | upper }}</span>
{% if m.host_id %}
<a class="ping-name" href="/hosts/{{ m.host_id }}" title="{{ m.name }}"
style="color:inherit;text-decoration:none;">{{ m.name }}</a>
{% else %}
<span class="ping-name" title="{{ m.name }}">{{ m.name }}</span>
{% endif %}
<span class="ping-addr" title="{{ m.target }}">{{ m.target }}</span>
</div>
<div class="ping-pills">
{% for _ in range([30 - d.heartbeat|length, 0]|max) %}
<span class="pill pill-empty" title="No data"></span>
{% endfor %}
{% for h in d.heartbeat %}
<span class="pill" title="{{ h.title }}"
style="background:{{ '#1a6632' if h.state == 'up' else '#6a1515' }};"></span>
{% endfor %}
</div>
<div class="ping-cur">
{% if latest is none %}
<span class="ping-cur-nd">—</span>
{% elif not latest.is_up %}
<span class="ping-cur-down">DOWN</span>
{% elif latest.response_ms is not none %}
<span style="{{ threshold_style(latest.response_ms, 'latency') or 'color:var(--green);' }}">
{{ "%.0f"|format(latest.response_ms) }} ms</span>
{% elif m.type == 'dns' and latest.resolved_ip %}
<span class="ping-cur-good" style="font-size:0.72rem;">{{ latest.resolved_ip }}</span>
{% else %}
<span class="ping-cur-good">UP</span>
{% endif %}
</div>
<div style="min-width:4.5rem;text-align:right;font-size:0.78rem;font-variant-numeric:tabular-nums;{{ threshold_style(d.uptime_pct, 'uptime') if d.uptime_pct is not none else 'color:var(--text-dim);' }}">
{% if d.uptime_pct is not none %}{{ "%.1f"|format(d.uptime_pct) }}%{% else %}—{% endif %}
<span style="color:var(--text-dim);font-size:0.7rem;margin-left:0.15rem;">{{ range_key }}</span>
</div>
{% if d.tls_days is not none %}
<div title="TLS certificate expiry" style="min-width:4rem;text-align:right;font-size:0.72rem;{{ 'color:var(--red);' if d.tls_days < 14 else ('color:var(--yellow);' if d.tls_days < 30 else 'color:var(--text-dim);') }}">
cert {{ d.tls_days|round|int }}d
</div>
{% endif %}
{% if session.user_role in ['operator', 'admin'] %}
<div style="display:flex;gap:0.35rem;flex-shrink:0;">
<a href="/monitors/{{ m.id }}/edit" class="btn btn-sm" style="padding:0.2rem 0.5rem;font-size:0.72rem;">Edit</a>
<form method="post" action="/monitors/{{ m.id }}/toggle" style="display:inline;">{{ csrf_field() }}
<button type="submit" class="btn btn-sm" style="padding:0.2rem 0.5rem;font-size:0.72rem;">
{{ "Pause" if m.enabled else "Resume" }}</button>
</form>
<form method="post" action="/monitors/{{ m.id }}/delete" style="display:inline;"
onsubmit="return confirm('Delete monitor {{ m.name }}?');">{{ csrf_field() }}
<button type="submit" class="btn btn-sm" style="padding:0.2rem 0.5rem;font-size:0.72rem;color:var(--red);">✕</button>
</form>
</div>
{% endif %}
</div>
{% endfor %}
{% else %}
<p style="color:var(--text-muted);font-size:0.9rem;padding:1rem 0;">
No monitors yet. Add one above, or attach checks to a host from the
<a href="/hosts/" style="color:var(--accent);">Hosts</a> page.
</p>
{% endif %}