Skip to content

v1.6 — Audit Remediation

Close the open findings from the 2026-04-15 full-monorepo audit before any new feature work — security, performance, architecture, frontend quality, and cross-app reliability.

Summary

A monorepo-wide audit conducted on 2026-04-15 catalogued 31 issues spanning leaked secrets in git history, N+1 queries on the auth path, GraphQL resolvers without query-depth limits, missing rescue blocks on background jobs, and frontend code without coverage gates. v1.6 ran the entire backlog to ground in a single milestone so v1.7 and v1.8 could land on a stable platform.

The work was deliberately defensive — no user-visible features changed. What changed: secrets were scrubbed from history, CORS and security headers were locked down, the unified feed query was rewritten as a single SQL UNION ALL (was four in-memory concatenations), Apollo gained typed cache policies and runtime error handling, and a CI schema-drift gate now fails any PR that ships GraphQL changes without regenerated TypeScript types.

The milestone shipped on 2026-04-16, one day after the audit was filed.

Goal

Resolve every CRITICAL and HIGH finding from the 2026-04-15 audit across security, performance, architecture, frontend quality, and cross-app reliability — and add the missing CI gates that would have caught each finding earlier.

Scope — What Shipped

Security (Phase 21)

  • Scrubbed .env files from git history via git filter-repo
  • Comprehensive .gitignore covering secrets, credentials, OS files, dependency caches
  • CORS allowlist locked to known production and staging domains
  • Security headers middleware (CSP, HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy) on Rails + Firebase
  • HMAC-SHA256 auth + rate limiting on Agent Runner endpoints
  • npm audit + bundle audit wired into CI

Performance (Phase 22)

  • N+1 on auth path fixed (.pluck(:role_key).include?(role).exists?(role_key: role))
  • BuildUnifiedFeed rewritten as a single SQL UNION ALL — previously four in-memory source concatenations
  • GraphQL hardening: max_query_depth: 5, default_max_page_size: 10, 30-second execution timeout
  • bullet gem enabled in development for live N+1 detection
  • Badge stats cache invalidation moved to a UserAction after_create callback

Architecture (Phase 23)

  • query_type.rb decomposed from 720 lines to 270 via 7 extracted resolver modules (Goal, Community, User, Admin, Feedback, AiWorkforce, Feed)
  • 13 rescue StandardError blocks now report to Sentry instead of swallowing
  • All 19 background jobs given rescue_from + Sentry capture
  • Community model gained validations missing since launch
  • trending_communities, recommended_communities, and community_insights resolvers replaced their stubs with real queries (member growth windows, goal-category matching, post/comment counts)

Frontend Quality (Phase 24)

  • Apollo InMemoryCache typePolicies for Goal/Community/User keyed by publicId
  • any types removed from apollo-client.ts and useApiError.ts
  • ESLint rule @typescript-eslint/no-explicit-any: warn enabled
  • Three reusable GraphQL fragments (GoalFragment, UserFragment, CommentFragment) wired into 4 mutations
  • Vitest 80% line / 75% branch coverage thresholds enforced in CI
  • GoalDetails.vue extracted as a 275-line child component (parent refactor deferred — see Tech Debt)

Cross-App & Reliability (Phase 25)

  • graphql:schema:dump rake task exports SDL on every backend change
  • graphql-codegen configured in both ionic_frontend and admin_dashboard with generated TypeScript types
  • CI schema-drift job runs git diff --exit-code on the generated types
  • Ai::AgentRunnerUnreachable typed error + 3-attempt circuit breaker on ExecuteRunJob
  • sidekiq_options retry: 5 on three external-dependency jobs
  • useTheme composable refactored to a module-scoped singleton (10 identity-equality assertions in tests)

Phases

PhaseNameStatusPlansHighlights
21Security HardeningShipped4Secrets scrubbed, CORS + headers, HMAC + rate limits, audit gates in CI
22Performance & Query OptimizationShipped4UNION ALL feed, GraphQL depth/timeout, N+1 auth fix, bullet in dev
23Architecture CleanupShipped4query_type decomposition, Sentry-everywhere, real community resolvers
24Frontend QualityShipped3typePolicies, fragments, coverage thresholds, GoalDetails extraction
25Cross-App & ReliabilityShipped3codegen + schema-drift CI gate, circuit breaker, theme singleton

Key Decisions

  • Filter-repo over rotation. The leaked .env files contained pre-launch secrets that had already been rotated; scrubbing history was preferred over leaving them visible in git log.
  • UNION ALL over a materialized view. The feed query's hot path is read-once-per-session; UNION ALL with index hits beat the operational complexity of a refresh job.
  • Coverage thresholds in CI, not local. Local thresholds slowed inner-loop development; CI enforcement gates merges without blocking dev iteration.
  • Codegen + schema-drift as the long-term gate. Replaces ad-hoc reviews of generated types with a git diff --exit-code job — drift is now impossible to merge.

Requirements Coverage

31 / 31 requirements satisfied

CategoryCountStatus
SEC-*8All satisfied
PERF-*7All satisfied
ARCH-*6All satisfied
FE-*6All satisfied (FE-02, FE-03 with deferred follow-ups — see Tech Debt)
SCALE-*4All satisfied

Outcomes

The platform now refuses merges that drift schema, ship any in core Apollo files, drop coverage below 80%, or expose endpoints without security headers. Every existing background job reports its failures to Sentry. The unified feed renders in a single SQL round-trip. New CRITICAL or HIGH audit findings would surface in CI before review.

Tech Debt

  • (FE-02) OnboardingWizard + GoalForm unit tests — agent ended mid-work; tests landed later in v1.8.
  • (FE-03) Wire GoalDetails.vue into Goal.vue — manual refactor deferred to a future polish window.
  • (SEC-01) Manual key rotation in Clerk + GCP dashboards — operator task, tracked outside the codebase.
  • b9cd0b44 — Gsd/v1.6 audit remediation (#323)
  • 3aaec5a5 — chore: complete v1.6 milestone — archive and reset for next cycle

Last updated: 2026-05-22

Loading…