feat (cloud): cloud bridge + shared session plugin + guest-report backend#663
Open
arberx wants to merge 5 commits into
Open
feat (cloud): cloud bridge + shared session plugin + guest-report backend#663arberx wants to merge 5 commits into
arberx wants to merge 5 commits into
Conversation
…gin + guest-report backend Rebases the long-running aero-owner-view work onto current main (the /aero UI experiment and cloud-mode SPA churn are dropped — net backend only). The 5 hosted migrations are renumbered v73–77 to sit cleanly after main's GBP migrations (v67–72). What this adds (all additive, OSS-safe, env-gated off by default): - Track 3 cloud bridge — `/api/v1/cloud/*` routes (bootstrap, events, google-tokens, bing-key) gated behind `CANONRY_ENABLE_CLOUD_BOOTSTRAP` + `X-Admin-Scope`; return 404 in OSS. `cloud_metadata` singleton table. Outbound `emitConnectionEvent`/`emitCloudEvent` signed webhooks — inert with no subscriber (standalone OSS emits nothing). - Track 1 cloud runtime — `provider_token_usage` telemetry table, populated from provider usage blocks; read by the control plane for billing. - Session plugin — extracted `/session*` into packages/api-routes so the local daemon and apps/api (Cloud Run) share it; apps/api persists the dashboard password in the new `app_settings` table. - Guest-report backend — `/api/v1/guest/report*` (anonymous /aero audit + SSE), `users` + `guest_reports` tables. Backend only; the /aero UI ships on a separate track. - Quota-lease client (OSS→control-plane), degrades to standalone when no control plane is configured. - Tests: cloud bootstrap/bing/google + outbound emit gating & envelope contract + session + guest-report. Full suite green (4110 tests). Migrations renumbered once (not per-commit) to avoid version collisions with main. SDK regenerated. Version 4.67.0 → 4.68.0.
| const DASHBOARD_PASSWORD_KEY = 'dashboard_password_hash' | ||
|
|
||
| function hashApiKey(key: string): string { | ||
| return crypto.createHash('sha256').update(key).digest('hex') |
| const DASHBOARD_SCRYPT_MAXMEM = 64 * 1024 * 1024 | ||
|
|
||
| function hashApiKey(key: string): string { | ||
| return crypto.createHash('sha256').update(key).digest('hex') |
Resolves the legit findings from the GitHub Advanced Security review: - ReDoS (CodeQL #74) — `normalizeDomain` (anonymous /guest/report) rewritten from a backtracking regex to linear string ops (indexOf/slice/split). Added a unit test that a 200k-char all-slashes input returns in <100ms. - Missing rate limiting (CodeQL #78/#79/#80) — added a small per-registration in-memory fixed-window limiter (`createRateLimiter` in helpers.ts) and wired it to the brute-force-exposed routes: password login + first-time setup (10/min), logout (30/min), guest-report create (15/min) + claim (20/min). Limits sit well above legitimate use; tests cover trip-at-11 and a normal burst staying under the cap. Documented (not changed) the two "insufficient hash effort" findings (CodeQL #75/#76): `hashApiKey` SHA-256 is correct for 128-bit random `cnry_` tokens (no wordlist to brute-force; a slow KDF would only add per-request latency). Dashboard passwords already use salted scrypt. False positive for opaque tokens — added a comment in session.ts + apps/api/app.ts. No version bump (same unreleased 4.68.0 PR). api-routes suite: 1153 pass.
Member
Author
|
Addressed the CodeQL review in f2c5806: Fixed
Won't fix — false positive (documented in code)
Suggest dismissing #75/#76 as "won't fix (false positive — opaque token, not a password)". |
…limiting The re-scan kept flagging "missing rate limiting" (#81/#82/#83) on the login / logout / guest-claim handlers — CodeQL's query only recognizes known rate-limit libraries, not the custom in-handler limiter from the previous commit. Swap to @fastify/rate-limit (Fastify 5 → v10), which CodeQL detects. - Registered scoped to the session + guest-report plugin encapsulations only, so the main dashboard/API routes are untouched. In-memory store matches the single-tenant posture. - Per-route caps via `config.rateLimit`: login + setup 10/min, logout 30/min (plugin default), guest create 15/min, claim 20/min, guest reads 60/min. - Removed the custom `createRateLimiter` from helpers.ts (now unused). Same brute-force behavior, now via a CodeQL-recognized mechanism. Tests unchanged and green (login trips 429 after the cap). 2431 tests pass across api-routes + canonry + apps/api; typecheck + lint clean.
Member
Author
|
Follow-up in e8263a9 — the re-scan kept flagging "missing rate limiting" (#81/#82/#83) because CodeQL only recognizes known rate-limit libraries, not the custom in-handler limiter I added first. Swapped to
Same brute-force behavior, now via a mechanism CodeQL recognizes — these three should clear on the next scan. 2431 tests pass; typecheck + lint clean. |
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.
Adds the OSS-side foundation for Canonry Hosted — all additive and env-gated off, so OSS/local behavior is unchanged. Rebased onto current main; the
/aeroUI experiment and cloud-mode SPA churn from the original branch are dropped (net backend only).What's included
/api/v1/cloud/*routes (bootstrap, events, google-tokens, bing-key) gated behindCANONRY_ENABLE_CLOUD_BOOTSTRAP+X-Admin-Scope(return 404 in OSS, fingerprint-resistant).cloud_metadatasingleton table. OutboundemitCloudEvent/emitConnectionEventsigned webhooks — inert when no subscriber exists.provider_token_usagetelemetry table, populated from provider usage blocks; consumed by the control plane for billing./session*extracted intopackages/api-routesso the local daemon andapps/api(Cloud Run) share it;apps/apipersists the dashboard password in the newapp_settingstable./api/v1/guest/report*(anonymous audit + SSE),users+guest_reportstables. Backend only; the/aeroUI ships separately.Migrations
The 5 hosted migrations are renumbered v73–77 to sit after main's GBP migrations (v67–72) — no version collisions.
Tests
Cloud bootstrap/bing/google routes, outbound emit gating + envelope contract, session, and guest-report all covered. Full suite green: 4110 tests pass, typecheck + lint clean. Version 4.67.0 → 4.68.0.
🤖 Generated with Claude Code