v1.1.0: session scoring + security hardening + scoring-recv exclusion override#4
Merged
Conversation
Squashed from the working set on session-scoring. Covers the session scoring + dashboard performance work from the prior squash baseline plus the recent additions: - DUCKDB_POOL_MAX_SIZE env knob (was hardcoded to 8 per service) - run.sh: compose NODE_OPTIONS instead of clobbering, refuse to bind ports commonly used by SSH tunnels to a remote backend/frontend - Dashboard stale-view retry: detect when /api/dashboard/aggregates returns inconsistent results (metadata reports recent logs but every aggregation comes back empty) and let React Query retry up to twice. Mitigates the intermittent "no data" symptom during metadata_sync cron ticks; doesn't address the underlying writer contention. - scripts/dev/sync-from-remote.sh: developer-only tool that mirrors a remote data tree locally and scrubs credentials/crons in the copied configs so the local backend can serve the synced volume without writing back. - .vscode/ added to .gitignore (local editor config). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
CHANGELOG.md - Drop incorrect "next 16.2.6 → 16.2.7" and "eslint-config-next 16.2.6 → 16.2.7" bumps; both stayed pinned at 16.2.6. Phrasing for the react/react-dom resolved bump corrected. - Performance section now names the three structural workstreams that landed alongside the smaller tuning bullets: the DuckDB connection pool, the hourly Top-N rollup pipeline, and the bounded-cache primitive. README.md - Replace broken reference to `sample-vcl.vcl` (the file does not exist in the repo) with a description of what the manual-setup VCL needs to do and how to source it from the wizard's output. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
v1.1.0: session scoring + security hardening + scoring-recv exclusion override
This branch ships v1.1.0, squashed from 70+ original commits across
four workstreams (sections below): session scoring, security
hardening, scoring-recv URL exclusion override, and a tranche of
performance / reliability / operational changes. 240+ files changed;
make cigreen (lint + format + mypy + 3,053 backend pytest + 9 vclno-vulns). Deployed and verified on the dev VM.
1 — Session scoring
End-to-end edge anomaly-detection pipeline for Fastly Compute. Layer 1
behavioural (cookie compliance, impossibly-fast browsing, robotic
dwell) + Layer 2 transition-matrix scoring + combined 0–100 quantized
score. Dual-implemented in Python (backend/scoring) and Rust
(compute/scorer); cross-language wire-format tests pin the AES-GCM
cookie codec byte-for-byte.
deliver / miss / enforce), AES-GCM cookie carrying rotating sid +
transition state,
fastly.ddos_detectedbypass.labelled-session retrain loop, /scoring/evaluation + /scoring/health
AES key rotation with grace window, sliding cookie lifetime,
scoring audit log, threshold enforcement that 429s flagged requests
at the edge within seconds of commit.
against accumulated labels, ScoringHealthCard, ThresholdSlider with
counterfactual flag/pass preview + precision/recall, RocPrCurves,
TopFlaggedTable, LabelsTab with click-to-view-events, RetrainButton,
RotateKeyButton, MatrixVersionsCard, per-reason AUC breakdown,
session-events viewer, ExcludeRegexCard (see §3), help popups.
See docs/session_scoring_runbook.md + docs/features.md for the runbook
and feature reference.
2 — Security hardening
Comprehensive hardening across the FastAPI backend, Fastly VCL, Next.js
frontend, and Rust scorer. Full breakdown in the
### Securityblockof CHANGELOG.md 1.1.0; capability summary:
--proxy-headers+--forwarded-allow-ips=127.0.0.1, Caddy peer-IP gating ofFastly-Client-IP → XFFrewrite, Caddy-injectedX-Proxied-By-Caddymarker driving Next.js/admingating in placeof the (forgeable) Host header. Backend reads request.client.host
as the trust signal everywhere; in-app leftmost-XFF parsing is gone.
mutation, and NGWAF workspace listing all require a caller-supplied
Fastly token validated via /tokens/self for
globalscope andservice binding. Server-stored credentials are never used as a
destructive-op fallback.
every /api/query call with a statement-type whitelist + recursive
parse-tree walker (catalog + function blocklists, fail-closed parse,
audit logging, perf budget). Replaces an incomplete regex-based
blocklist that missed read_csv_auto, information_schema,
duckdb_secrets, INSTALL/LOAD, and getenv. Plus escape_sql_literal
helper applied across ingest sites with characterisation tests for
the audit-PoC payload, multi-byte UTF-8, backslash, and empty inputs.
client-spoofable internal x-of-* / x-fos-edge-data /
x-is-cluster-fetch / X-Edge-* header; origin-metric log fields are
numeric-regex-gated and json.escape-wrapped; CDN vcl_hash keys on
full req.url; CDN vcl_recv now also runs querystring.filter_except
(S3-API allow-list) + querystring.sort so unexpected params can't
fracture the cache or leak the auth key into req.hash.
filter every read by analyst-session service_ids and gate every
mutation with pre-flight get_alert_by_id / get_view_by_id lookups so
unauthorised mutations never land. Cache layer audited: every
per-tenant cache key includes service_id.
realpath + commonpath check; service_id alphanumeric/dash regex at
path helpers; bucket-name separator rejection in cleanup.
with atomic UPDATE-with-rowcount; quarantine narrowed to actual
SQLite corruption signatures (was wiping the DB on any
OperationalError); scrypt timing equalised across hit/miss to close
the email-enumeration oracle; rate-limiter dicts bounded; stack-
trace key stripped from HTTPException.detail with sweep fixture
asserting no route leaks tracebacks.
loader; the tunnel manager refuses to start if the pinned file is
missing/empty.
L1_SCORE_COOKIE_TAMPERED=100 (was capped at 75 alongside
missing/expired), L1_ROBOTIC_DWELL_LOW_S 0.5 → 0.20 (closes the
0.20s–0.50s dwell-band evasion). Sliding-window mean (cookie schema
v3) tracked as a 1.2 follow-up.
3 — Scoring-recv URL exclusion regex (operator control)
The Compute scorer skips requests whose URL matches a configurable
exclusion regex. A default static-asset extension list (
.css,.js,.png, …) ships as the fallback so common asset traffic bypasses thescorer out of the box; operators override it per-service from the
Session Scoring page to add patterns (health checks, internal
endpoints, etc.) without touching code or VCL.
exclude_url_regex parameter; persisted in
cfg.scoring.exclude_url_regex (None / "" = use default).
update_recv_exclusion_regex orchestrator clones only the active
version, swaps the recv snippet, validates, activates — ~5–15s vs.
the full enable_scoring flow.
(returns current + default + effective) and PUT
/api/services/{id}/scoring/exclude-regex?confirm=true (token-gated;
audit-logged as scoring_exclude_regex_changed).
chars, must compile under Python re.
assembled recv snippet (catches composition errors that slip past
Python's compiler).
pre-populated with current value, "Show default" toggle, "Reset to
default" button, inline lint-error display, confirm-dialog before
publish.
sets SCORING_REQUIRE_FALCO=1 so a missing binary fails closed
instead of degrading to input-policy-only.
4 — Performance, reliability, and operational changes
A tranche of supporting work that doesn't fit cleanly into §1–§3 but
is in the squash:
Dashboard performance
backend/core/duckdb_pool.py— DuckDB connection pool replacingper-request connection setup.
backend/core/rollups.py+scripts/backfill_rollups.py— rollupprecomputation pipeline for the dashboard's hot aggregates.
backend/utils/bounded_cache.py— generic TTL + maxsize cache(
tests/utils/test_bounded_cache.py, 13 tests), used to bound therate-limiter and several previously-unbounded dict caches.
loading.tsxfiles across the Next.js app routes, plusfrontend/components/skeletons/PageSkeleton.tsx,frontend/components/LazyMount.tsx,frontend/lib/staleViewRetry.ts,frontend/hooks/useNowSeconds.tsfor streaming SSR fallbacks and stale-view recovery.
backend/repositories/dashboard.py(+346),backend/repositories/_base.py(+261),backend/state_sync.py(+268),
backend/scheduler.py(+229),backend/core/metadata_db.py(+893).Operational tooling
backend/core/data_migrations.py— schema migration framework runon backend startup.
scripts/dev/sync-from-remote.sh— pull GCE deployment data into ascrubbed local working copy for performance work or feature dev
against realistic volumes.
frontend/middleware.tsremoved; behaviour moved tofrontend/proxy.tsfor the Caddy-marker / analyst-session split.docs/demo_production_guide.md— runbook for the demo deployment.Supporting modules surfaced during §2 / §3 work
backend/utils/vcl_validator.py(+tests/utils/test_vcl_validator.py)— the falco-backed VCL static-analysis stage used by the scoring
orchestrator.
backend/utils/fastly_auth.py— extracted Fastly token-validationhelper used by the destructive-op auth checks.
Session scoring additions beyond §1's named components
FlagSessionPopover, PerReasonAucCard, ScoreDistChart,
SessionEventsDialog, SinceHoursPicker, StackedHourlyBarChart,
per-component help-content modules.
/scoring/score-distribution,/scoring/compliance-breakdown,/scoring/enforce-threshold(GET+PUT),
/scoring/enforce-status-code(GET+PUT),/scoring/threshold(GET+PUT),/scoring/evaluation/per-reason,/scoring/matrix-versions/{version}/restore,/scoring/exclude-regex/validate, plus the/scoring/labelsPOST/PATCH/DELETE mutations behind the read endpoint.
compute/scorer/matrix.default.jsonships as the default trainedmatrix;
scripts/scoring/{deploy_wasm,extract_traces,train}.sh|.pyare the training / deploy tooling.
Tooling additions
(.pre-commit-config.yaml), make secret-scan (chained into make ci),
and .github/workflows/ci.yml. Configuration in .gitleaks.toml
extends the default ruleset with path allowlists for tracked test
fixtures, Rust lockfile checksums, the public SSH host key, and
gitignored runtime directories. AGENTS.md §Secrets documents the
policy and suppression playbook.
Infrastructure
python:3.12-slim-bookworm. The frontend runtime stays on
node:24-slim; only the multi-stage api-schema build inherits the
Python bookworm bump. Remaining base-image CVEs are deep-dependency
/ OpenSSL CVEs every major Python base inherits.
snippet validator.
distlib, filelock, idna 3.17 → 3.18, joserfc 1.6.8 → 1.7.0.
@types/react 19.2.15 → 19.2.16, react / react-dom resolved to
19.2.7 via the existing
^19.2.5range. next + eslint-config-nextstay pinned at 16.2.6.
(compiler-API breaking changes); Fastly Rust SDK 0.11 → 0.12
(Compute@Edge API churn); jsdom / eslint / vitest where we're
already ahead of the npm "latest" tag.
Versioning
Bumped to 1.1.0 in pyproject.toml, frontend/package.json, and the
FastAPI app.version. CHANGELOG updated under [1.1.0] - 2026-06-03
with Security + Infrastructure sections.
Test coverage
backend pytest 3,053
Rust scorer 65 (+8)
frontend vitest 265 (+13)
VCL tests 9 (same)
New test files for this release (test-function count):
tests/utils/test_sql_validator.py 27
tests/utils/test_vcl_validator.py 18
tests/test_proxy_headers_regression.py 7
tests/test_no_trace_leakage_sweep.py 3
tests/routers/test_provision_teardown_auth.py 11
tests/routers/test_cross_tenant_scope.py 9
tests/routers/test_scoring_exclude_regex.py 15
tests/utils/test_bounded_cache.py 13 (see §4)
Notes for reviewers
git reflog locally. The squash makes this reviewable as one
semantic unit (v1.1.0 release) instead of paging through unrelated
intermediate work.
snapshot regenerated.
main with v1.1.0 rather than the PR branch.
Test plan
make cipasses locallythree containers healthy, GET /api/health returns 200
+ sort + req.url cache key
exclusion regex card, paste a custom regex (e.g. .(healthz)$),
click Save → publish flow completes
Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com