CVE-2025-69720 (ncurses), CVE-2026-27135 (nghttp2), and CVE-2026-29111
(systemd) have no upstream fix available. .trivyignore suppresses them
so Trivy can still gate on all other CRITICAL/HIGH findings without
relying on the coarser ignore-unfixed flag in the workflow.
Resolves dependabot PRs #30 and #31 which had merge conflicts after
the Flask-Limiter v4 and email-validator bumps landed. Flask-Login
0.6.3 adds Flask 3 / Werkzeug 3 compatibility.
Intercepts form submit via fetch and stores failed submissions in
IndexedDB when offline. Replays queued entries on the online event and
on each page load. Shows an offline banner on the form page and a
sync-pending message on the thank-you page.
Service worker bumped to guestbook-v2 to pre-cache offline-queue.js
so the script is available when the kiosk has no network.
Returns the current session CSRF token as JSON so the offline queue
replay can obtain a fresh token without parsing HTML. Rate-limited to
30 requests/minute. Passes offline flag through to the thank-you
template.
Displays total entries, this week, this month, and newsletter opt-in
count and percentage at the top of the admin view. Week/month
boundaries are computed in America/Denver time and converted to UTC
for SQLite comparison, handling DST correctly.
Redirects to /thank-you?name=<first_name> on successful submission
instead of back to the blank form. Shows a 4-second countdown with
meta-refresh fallback before returning to the form. Includes the
scrolling guest marquee so the page feels consistent with the kiosk.
Serves /manifest.webmanifest dynamically from Flask so SITE_TITLE and
LOGO_URL env vars flow into the manifest at runtime. Serves /sw.js from
/static/sw.js with Service-Worker-Allowed: / header to allow root scope.
Service worker caches static assets and passes all app routes to network.
Runs aquasecurity/trivy-action after the build step and fails the
workflow if any CRITICAL or HIGH severity vulnerabilities are found,
blocking the push to Docker Hub.
set -e ensures the script aborts on any error (e.g. failed chown)
rather than silently continuing. --timeout 30 kills hung workers to
prevent slow-client attacks from exhausting the worker pool.
Adds FIELD_MAX constants and server-side length checks in the index
route. Adds matching maxlength attributes on all form inputs so the
browser enforces limits before submission.
Installs Flask-WTF and enables CSRFProtect globally. Adds csrf_token
hidden fields to all four POST forms (login, delete entry, add user,
delete user, and the public guestbook form). Exempts the API endpoint
which uses header-based key auth instead.
Sets X-Content-Type-Options, X-Frame-Options, and Referrer-Policy on
all responses. Prevents browsers from caching admin pages. Configures
session cookies as HttpOnly and SameSite=Lax with an 8-hour lifetime.
Limits POST to /admin/login to 10 requests/minute to block brute-force
attacks. Limits GET /api/guests to 100 requests/hour to prevent bulk
data exfiltration.
Raises RuntimeError at startup instead of silently falling back to a
hardcoded default, preventing misconfigured deployments from running
with a publicly-known session key.
Add weekly Dependabot updates for pip, Docker, and GitHub Actions.
Add issue templates for bug reports, feature requests, documentation,
and general feedback.
Convert UTC timestamps from SQLite to Mountain Time (America/Denver)
using a Jinja2 template filter backed by zoneinfo; add tzdata dependency
for IANA timezone data in the slim Docker image.
Replaces browser-cached Basic Auth credentials with proper server-side
session management. Logout now fully invalidates the session. Adds an
HTML login form at /admin/login, SECRET_KEY env var support, and updates
README with key generation instructions and role table.
Entrypoint now runs as root, chowns the data directory to appuser,
then drops privileges via gosu before starting Gunicorn. This prevents
sqlite3.OperationalError on mounted volumes owned by root.
- Add MIGRATIONS list — each entry is a list of SQL statements for
that schema version; append new lists to add future migrations,
never modify existing ones
- Add schema_version table to track applied migrations
- migrate_db() runs on startup and applies any pending versions
automatically; safe to run against existing DBs (v1 uses
CREATE IF NOT EXISTS so it no-ops on the existing table/indexes)
- entrypoint.sh: use GUNICORN_WORKERS to match example.env (#17)
- guestbook_export.py: read DATABASE_PATH from env instead of
hardcoded relative path (#18)
- Scrolling marquee: duplicate guest list for seamless loop,
animate translateX(0) to translateX(-50%), increase font to
1.25rem, fix JS speed calc to use half content width (#20)
Add a secondary normalized substring check: strips all non-alpha chars
then checks if any banned word appears as a substring. This catches:
- Spacing tricks: 'f u c k'
- Embedded forms: 'fucking'
Note: substring matching can produce false positives (e.g. 'classic'
contains 'ass'). Trade-off accepted for a museum kiosk context.