Skip to content

fix(logging): remove PII/pastoral-note debug logging from src (F5) - #84

Merged
chriskehayias merged 2 commits into
mainfrom
fix/logging-remove-pii-debug-output
Sep 12, 2026
Merged

chriskehayias merged 2 commits into
mainfrom
fix/logging-remove-pii-debug-output

Conversation

@chriskehayias

Copy link
Copy Markdown
Contributor

Finding

F5 (Medium), from the 2026-09-12 auth security review: member PII and pastoral notes were written to server logs at info level (console.log/.debug/.info), including full MP result sets, request bodies, and $filter query strings. Hosting/log-aggregation platforms retain this with broader access and longer retention than the MP database itself.

Policy

  1. Removed all debug/info-level logging (console.log, .debug, .info) from non-test source under src/, except the generator scripts under src/lib/providers/ministry-platform/scripts/ (CLI tools, left alone).
  2. Kept error logging that helps track down failures, but made it safe: console.error/console.warn in catch blocks now log identifiers and shape only (table name, status, endpoint path, error message) — never MP result sets, request bodies, Notes, emails, phones, names, or a URL/query string containing $filter.
  3. No logging library introduced.
  4. Coverage thresholds re-verified after the removals.

Files touched

File Removed Kept / made safe
src/lib/providers/ministry-platform/services/table.service.ts Debug logs: table name, $filter params, full fetched result sets console.error(message, error) in each catch — safe now that the error object never carries a body/URL (see http-client.ts)
src/lib/providers/ministry-platform/utils/http-client.ts PUT debug log (full URL + JSON body) GET/PUT failure logs now { method, endpoint (path only), status, statusText } — no responseBody, no full URL. GET's thrown Error message no longer appends response text
src/lib/providers/ministry-platform/client.ts Token-validity chatter (checking/expiry/refreshed) console.error("Failed to refresh MP access token:", error) unchanged/safe
src/lib/providers/ministry-platform/services/procedure.service.ts Procedure name/params/results dumps console.error per catch, unchanged/safe
src/proxy.ts Per-request path logging (allow/redirect chatter) Session-check catch now logs error instanceof Error ? error.message : String(error) instead of the raw error
src/app/signin/page.tsx Client-side callbackUrl/redirect-state logs
src/components/contact-logs/actions.ts Full Notes/record JSON dumps on create/update/delete + success chatter console.error("Error X:", error) per action, unchanged/safe
src/components/contact-logs/contact-logs.tsx Client-side create/update payload logs console.error calls now log the already-derived errorMessage string instead of the raw error/err
src/services/contactLogService.ts Full Notes/record JSON dumps + MP-TZ date chatter No catch blocks of its own (errors propagate to the action layer)
src/lib/auth.ts resolveMpUserId's catch now logs { userGuid, err: err instanceof Error ? err.message : String(err) } instead of the raw err object

The four structured events are unchanged and remain the greppable contract: mp.read.unauthorized / mp.write.unauthorized (authorizationService.ts), mp.write.non_user (sessionContextService.ts), auth.userinfo.invalid_sub (auth.ts).

Docs

  • .claude/references/auth.md: added a "Logging policy" note under Authorization, marked F5 closed.
  • CLAUDE.md: added a Key Development Practices item restating the no-debug-logging rule.
  • eslint.config.mjs: added a scoped "no-console": ["error", { allow: ["warn", "error"] }] rule over src/**/*.{ts,tsx}, exempting src/lib/providers/ministry-platform/scripts/** and *.test.{ts,tsx}. npm run lint passes cleanly with no unrelated fallout.

Tests

  • No existing test asserted on a removed console.log call or the old GET-failure message format, so none needed rewriting.
  • Added "Logging safety (F5)" negative-test coverage in table.service.test.ts, http-client.test.ts, contactLogService.test.ts, and contact-logs/actions.test.ts (creating/updating/deleting never logs Notes, even on a forced failure; a failed GET/PUT never logs or throws with the response body, full URL, or $filter).
  • proxy.test.ts: added a non-Error-thrown case to cover both arms of the safe-error-logging ternary.

Verification

  • npx vitest run --coverage: 912 tests passed, all thresholds met (statements 99.54%, branches 96.66%, functions 99.27%, lines 99.72%).
  • npx tsc --noEmit -p tsconfig.json: clean.
  • npm run lint: clean.
  • No calls of any kind made to the Ministry Platform API in the course of this change.

🤖 Generated with Claude Code

chriskehayias and others added 2 commits September 12, 2026 18:25
F5 (Medium, 2026-09-12 auth security review): member PII and pastoral
notes were written to server logs at info level via console.log/debug.
Hosting/log-aggregation platforms retain this with broader access and
longer retention than the MP database itself.

Removed (all console.log/.debug/.info in non-test, non-script src/):
- table.service.ts: table name, $filter query params, and full fetched
  result sets (contact names/emails/phones, dp_Users rows, Notes)
- http-client.ts: PUT request debug log (full URL + JSON body)
- client.ts: token-validity chatter ("Checking token validity...",
  expiry timestamps, "Token refreshed")
- procedure.service.ts: procedure name/params/results dumps
- proxy.ts: per-request path logging ("Allowing public path",
  "Allowing request to", "Redirecting to signin")
- signin/page.tsx: client-side callbackUrl/redirect-state logging
- contact-logs/actions.ts + contactLogService.ts: full Notes/record
  JSON.stringify dumps on create/update/delete, plus success chatter
- contact-logs.tsx: client-side create/update payload logging

Kept and made safe (identifiers/shape only, never content):
- table.service.ts / procedure.service.ts / client.ts: catch blocks
  still log via console.error(message, error) - the error object itself
  no longer carries any response body or full URL (see http-client.ts
  below), so this was already safe once that boundary was fixed.
- http-client.ts: GET/PUT failure logs now emit
  { method, endpoint (path only, no query string), status, statusText }
  - no responseBody, no full URL. The GET failure's thrown Error message
  no longer appends the response text (it could echo $filter or record
  content back); PUT's thrown message never included it.
- proxy.ts: session-check catch now logs
  error instanceof Error ? error.message : String(error) instead of the
  raw error.
- lib/auth.ts: resolveMpUserId's catch now logs
  { userGuid, err: err instanceof Error ? err.message : String(err) }
  instead of the raw err object (userGuid is an identifier, not content).
- getUserInfo's response.status log and the auth.userinfo.invalid_sub
  structured error were already safe; unchanged.
- The four structured events (mp.read.unauthorized, mp.write.unauthorized
  in authorizationService.ts, mp.write.non_user in
  sessionContextService.ts, auth.userinfo.invalid_sub in auth.ts) are
  unchanged - they already log identifiers only.

Tests:
- Updated no existing test asserted on a removed console.log call or on
  the old GET-failure message format (all pre-existing assertions used
  only status/statusText, never response text), so none needed rewriting.
- Added negative "Logging safety (F5)" coverage:
  - table.service.test.ts: fetching records logs nothing on success; a
    failure's console.error output never contains the $filter value or
    record content.
  - http-client.test.ts: a successful GET logs nothing; a failed GET's
    log and thrown message never contain the response body, full URL, or
    query string; a failed PUT's log never contains the request body.
  - contactLogService.test.ts / contact-logs/actions.test.ts: creating,
    updating, and deleting a contact log never logs Notes, including on
    a forced failure.
  - proxy.test.ts: added a non-Error-thrown case to cover both arms of
    the safe-error-logging ternary.

Verification: npx vitest run --coverage (912 tests, all thresholds met:
statements 99.54%, branches 96.66%, functions 99.27%, lines 99.72%);
npx tsc --noEmit -p tsconfig.json (clean); npm run lint (clean).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- .claude/references/auth.md: added a "Logging policy" note under the
  Authorization section (no debug/info logging in src/; errors log
  identifiers and shape only, never MP record content, filters, or
  request bodies; the four structured events remain the greppable
  contract) and marked F5 closed (2026-09-12) in the Closed findings
  table.
- CLAUDE.md: added item 12 under Key Development Practices restating
  the no-debug-logging rule for future contributors/agents.
- eslint.config.mjs: added a scoped "no-console": ["error", { allow:
  ["warn", "error"] }] rule over src/**/*.{ts,tsx}, with an override
  exempting src/lib/providers/ministry-platform/scripts/** (dev-only
  codegen CLI) and *.test.{ts,tsx} files. Verified npm run lint passes
  cleanly with no unrelated fallout, and that the rule actually fires
  (manually added a console.log to a src file, confirmed eslint flagged
  it, then reverted).

Verification: npm run lint (clean); npx tsc --noEmit -p tsconfig.json
(clean).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@chriskehayias
chriskehayias merged commit b4b55c4 into main Sep 12, 2026
2 checks passed
@chriskehayias
chriskehayias deleted the fix/logging-remove-pii-debug-output branch September 12, 2026 22:27
@codecov

codecov Bot commented Sep 12, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

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.

1 participant