Skip to content

Merge dev: app-review fixes (security, env validation, docs, licensing)#9

Merged
chriskehayias merged 20 commits into
mainfrom
dev
May 31, 2026
Merged

Merge dev: app-review fixes (security, env validation, docs, licensing)#9
chriskehayias merged 20 commits into
mainfrom
dev

Conversation

@chriskehayias
Copy link
Copy Markdown
Contributor

@

Summary

Merges dev into main. Includes the 9 application-review TODO fixes plus prior dev work (MP datetime handling at the boundary, dependency/CVE updates, README/env documentation).

Application-review fixes (this batch)

  • Security: removed PUT request-body console.log from the MP HTTP client; removed leftover debug console.logs across proxy/client/services (kept console.error).
  • Robustness: new getEnv() fail-fast env helper replacing process.env.X! assertions; sign-in getSession() now surfaces an error + retry instead of a perpetual spinner.
  • Embed: dropped the unused Idempotency-Key CORS header (no dedup implemented; standard jti claim retained for revocation).
  • Docs/meta: corrected stale CLAUDE.md (5 widgets, next-embed.es.js, ~33KB gzip, full services list); canonicalized project name to MPNext-Widgets; enforced Node >=20.9 via engines + .npmrc engine-strict; added UNLICENSED license field and proprietary LICENSE.

Prior dev work also included

  • Route MP datetimes through DomainTimezoneService at the boundary (+ tests, reference docs).
  • Dependency updates incl. 2 moderate CVE fixes; TypeScript / @types bumps.
  • README/env-var documentation corrections; removed hardcoded references.

Verification

Each fix passed pnpm lint and a Next.js build with no new failures.

🤖 Generated with Claude Code
@

chriskehayias and others added 20 commits May 30, 2026 16:08
MP stores and interprets datetime values as wall-clock in the domain's time zone, not UTC. Converting with raw new Date().toISOString() shifted date-boundary queries by the UTC offset.

- Add DomainTimezoneService singleton (domain TZ -> IANA, toMpSqlDatetime, parseMpDatetime) with unit tests
- Add getMpTimezone() server action for client-side Intl.DateTimeFormat rendering
- Route fullCalendar getEvents \ literals through toMpSqlDatetime (+ regression tests)
- Document the boundary-conversion rule and anti-patterns (CLAUDE.md, references, playbook)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Remove all hardcoded 'northwoods' tenant identifiers, my.northwoods.church hosts, and Northwoods-specific group ID defaults so the codebase is tenant-neutral.

- MP host: server resolves via getMpHost() (throws if MINISTRY_PLATFORM_BASE_URL unset); docs/snippets use neutral placeholder; widget falls back soft with a warning
- SDK build: drop church default, inject org name via VITE_ORG_NAME build define
- profile widget SMS consent text uses configurable org name
- Demo HTML: use existing __MP_BASE_URL__ placeholder; delete dead tid/initToken (session auth is origin-based)
- Group gates fail closed: remove '22'/'73' defaults; unset = no access + warning
- Docs: drop NorthwoodsNext/Northwoods-specific wording; fix stale CLAUDE.md tenant note
- Tests: neutral fixtures + fail-closed coverage

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two pre-existing tooling failures discovered during the Northwoods refactor, both unrelated to it:

#1 pnpm build:sdk: the SDK tsc step type-checked .test.ts files and vitest v4's vi.fn() mocks didn't satisfy ApiClientConfig's signatures. Give the api-client mocks precise Mock<T> types, and exclude *.test.ts from the production build tsconfig (tests don't belong in dist).

#2 pnpm lint crashed with 'TypeError: expand is not a function'. The pnpm override 'brace-expansion: >=5.0.5' forced 5.x (ESM named export) onto minimatch@3.1.5, which calls brace-expansion as a default function. Scope the override (minimatch@3>brace-expansion: 1.1.12) so minimatch@3 gets a patched v1 while minimatch@10 keeps 5.x. Both remain patched against the ReDoS advisory.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
With ESLint running again, 4 errors + 4 warnings (previously masked by the crash) surfaced. None originated from the Northwoods or datetime work.

- eslint config: ignore coverage/ (generated output should not be linted)
- (app)/layout.tsx: remove dead, unused mpBaseUrl
- access-denied.tsx: use next/link for the internal '/' page link
- demo/page.tsx: keep <a> for the /api/auth/sign-out API route (Link would prefetch and sign out early) with a scoped rule disable
- add-to-calendar route: drop unused 'claims' binding, keep the auth-check side effect
- fullCalendarService: remove unused EventParticipantCount interface; let -> const for ministryMap; justify the heterogeneous Promise<any>[] with a scoped disable

Verified: pnpm build, pnpm lint, and pnpm test:run (530 tests) all pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Security (pnpm.overrides):
- brace-expansion: override pinned the vulnerable 1.1.12; bump to 1.1.15
  (CVE-2026-33750, ReDoS/memory exhaustion via eslint>minimatch)
- postcss: add >=8.5.10 to float Next's bundled copy past the fix
  (CVE-2026-41305, XSS via unescaped </style> in stringify output)
pnpm audit now reports 0 vulnerabilities.

Safe updates: @types/react 19.2.15, vitest/@vitest/coverage-v8 4.1.7,
better-auth 1.6.12, postcss 8.5.15, react-hook-form 7.76.1, tsx 4.22.3,
typescript 6.0.3, vite 8.0.14, @hookform/resolvers 5.4.0,
@inquirer/prompts 8.5.1, @types/node 25.9.1, lucide-react 1.17.0,
zod aligned to 4.4.3 in @mpnext/types.

Workaround: pin kysely ^0.28.17 in overrides. better-auth 1.6.12 widened
its kysely peer range to ^0.29.0, but @better-auth/kysely-adapter's dist
still imports DEFAULT_MIGRATION_TABLE/DEFAULT_MIGRATION_LOCK_TABLE, which
kysely 0.29 removed — broke the Next build. Remove once upstream ships a
0.29-compatible adapter.

Verified: pnpm build (SDK + Next) green, pnpm lint clean,
pnpm test:run 530/530 passing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Raise the SDK's @types/node from ^20.19.37 to ^22.19.19 (latest 22.x,
Node 22 LTS), closing the gap with the Node 20 baseline. Intentionally
kept on the 22.x line rather than matching the root app's @types/node 25,
since the SDK targets a Node 22 LTS toolchain.

Dev-only type dependency for the Vite/TS toolchain; SDK source uses
DOM/Web Component APIs, not Node globals, so no type surface is affected.

Verified: tsc typecheck clean, vite build green (bundle byte-identical,
161.81 kB / 33.95 kB gzip), pnpm test:run 530/530 passing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Align @mpnext/embed-sdk and @mpnext/types from ^6 (resolved 6.0.2) to
6.0.3, matching the root app. Patch-level, dev-only.

Verified: pnpm build (SDK + Next) green, pnpm test:run 530/530 passing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Dev-only task runner used by the dev and test:widget scripts. v10's only
notable breaking change is dropping Node <20 (CI/dev run Node 24).

Verified: smoke-tested the exact flags the scripts use
(concurrently -n next,widget -c blue,green ...) — both processes labeled
and exit 0. pnpm build (SDK + Next) green, pnpm test:run 530/530 passing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Audited the codebase for every env reference (process.env, import.meta.env, VITE_*, NEXT_PUBLIC_*) and updated .env.example so all required and optional variables are present with explanatory comments. Added notes for the OIDC -> MINISTRY_PLATFORM_CLIENT_* fallback, Vercel auto-detected origins, and the auto-derived NEXT_PUBLIC_APP_VERSION.

Removed the legacy NEXTAUTH_URL / NEXTAUTH_SECRET fallbacks in favor of the canonical BETTER_AUTH_* names across auth, logout, the SDK Vite config, and the demo page.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Align README with the actual implementation: drop the unimplemented multi-tenant/idempotency claims, fix the SDK loader description (static hashed loader, not an x-sdk-hash header), bump the Node requirement to 20.9+, remove the nonexistent src/contexts/ entry, add the logout/session-tokens routes and domainTimezoneService, fix the ProfileService example, and correct the Claude Code commands table.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…curity)

The put() method logged the full request body via console.log on every
PUT, which could leak personal/sensitive data into production logs. PUT
flows through profile updates and other write paths. Removed the log;
remaining console.error calls only fire on failed responses.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Remove debug console.log noise on hot paths in the proxy and MP
provider client/table/procedure services. Intentional console.error
calls are retained.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add src/lib/env.ts with getEnv()/getEnvOptional() that throw a clear,
actionable error naming the missing variable, mirroring getMpHost().
Replace process.env.X! non-null assertions across the MP client,
client-credentials, auth, invoice service, and logout route so that
missing config fails fast instead of surfacing as cryptic later errors.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… perpetual spinner

The sign-in effect called authClient.getSession().then(...) with no rejection
handling. A network failure or auth-service outage left the user on a permanent
"Redirecting to sign in..." spinner with no feedback or recovery path.

Convert the effect to an inline async function with try/catch. On failure it
now surfaces a visible "Sign-in unavailable" message with a "Retry sign-in"
button that re-runs the flow via an attempt counter. A cancelled flag guards
against state updates / redirects after unmount. Also drops the previous
isRedirecting-in-deps pattern that could re-trigger OAuth.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…mented)

No embed route reads Idempotency-Key and no jti-based deduplication exists,
so advertising the header in Access-Control-Allow-Headers implied unsupported
behavior. Remove it from the three CORS allow-header lists in auth.ts, update
the matching test assertions, and drop the "idempotency keys" design claim
from CLAUDE.md. The standard jti JWT claim is retained (benign, unrelated).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… services list

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Align all project-name references to the canonical MPNext-Widgets
(matching the GitHub repo and git remote). Updates root package.json
name, CLAUDE.md title, .env.example NEXT_PUBLIC_APP_NAME, the app
metadata title, and setup script banners.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add an engines field (node >=20.9) to the root package.json, bump the
setup.ts Node check from major 18 to 20 (updating user-facing v18 message
strings to v20.9), and add a root .npmrc with engine-strict=true so pnpm/npm
hard-fail installs on unsupported Node. Aligns the Node policy across
package.json, setup.ts, CI (Node 22), and the README (v20.9+).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…file

Set license: UNLICENSED in root package.json, add a root LICENSE file
with a proprietary/confidential notice (ACS Technologies, 2026), and align
the README License section to reference the LICENSE file.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@chriskehayias chriskehayias merged commit 3113fd4 into main May 31, 2026
1 check passed
@codecov-commenter
Copy link
Copy Markdown

Welcome to Codecov 🎉

Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests.

ℹ️ You can also turn on project coverage checks and project coverage reporting on Pull Request comment

Thanks for integrating Codecov - We've got you covered ☂️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants