Skip to content

fix: redact sensitive audit log output - #95

Merged
humbeatbox merged 2 commits into
mainfrom
gary
Jul 6, 2026
Merged

fix: redact sensitive audit log output#95
humbeatbox merged 2 commits into
mainfrom
gary

Conversation

@humbeatbox

@humbeatbox humbeatbox commented Jul 5, 2026

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features

    • Added clearer claim status handling, including approval and rejection rules.
    • Introduced more detailed item status transition guidance, with supported expiry and disposal paths.
    • Added application logging for audit activity.
  • Bug Fixes

    • Updated claim approval behavior so linked items are marked claimed during the same update.
    • Improved conflict handling for items that already have an approved claim.
    • Ensured audit entries are logged when created.

@coderabbitai

coderabbitai Bot commented Jul 5, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Backend changes rename the claim-approval trigger condition from picked_up to approved, add a pino-based logger module, integrate audit logging into notification creation and the audit-log utility, update error messages in claims/items routes, and revise README documentation for claim/item status transitions.

Changes

Claim approval flow, logging, and documentation

Layer / File(s) Summary
Pino logger utility
backend/package.json, backend/src/lib/logger.ts
Adds pino dependency and a new logger export configured via LOG_LEVEL env var, defaulting to 'info'.
Claim approval transition and notification/audit logging
backend/src/routes/claims.ts
Changes the linked-item update trigger from picked_up to approved, updates the LINKED_ITEM_NOT_STORED conflict message, and adds an audit log entry for the notification created inside the transaction.
Audit log record capture and info logging
backend/src/utils/auditLog.ts
Captures the created audit log record and emits an info log with logId, action, actorId, entityType, and entityId.
Items status error message update
backend/src/routes/items.ts
Updates the 409 error text for items blocked by an approved claim, removing the “awaiting pickup” wording.
README documentation updates
backend/README.md
Documents the claims status PATCH endpoint, approval/rejection transaction semantics, and item status transition/blocking rules with cron job details.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant ClaimsRoute
  participant Prisma
  participant AuditLog

  Client->>ClaimsRoute: PATCH /api/claims/:claimId/status (approved)
  ClaimsRoute->>Prisma: begin transaction
  Prisma->>Prisma: update linked item to claimed
  Prisma->>Prisma: update claim status
  Prisma->>Prisma: create notification
  Prisma->>AuditLog: createAuditLog(notification data)
  AuditLog->>AuditLog: logger.info(logId, action, actorId, entityType, entityId)
  Prisma-->>ClaimsRoute: commit transaction
  ClaimsRoute-->>Client: updated claim detail
Loading

Suggested reviewers: hnam10

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title mentions redacting sensitive audit log output, but the diff mainly adds audit logging and logger infrastructure rather than redaction. Retitle it to reflect the actual change, such as adding audit logging and logger support for claim and item status updates.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch gary

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/src/routes/claims.ts (1)

1011-1032: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Guard the item transition with a conditional write. findUnique plus an unconditional update leaves a TOCTOU window here; two concurrent approvals can both observe stored and both flip the same item to claimed. Use the same updateMany({ where: { itemId, status: ItemStatus.stored } }) pattern already used elsewhere so only one approval can win.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/src/routes/claims.ts` around lines 1011 - 1032, The item status check
and update inside the prisma.$transaction block in claims.ts are vulnerable to a
TOCTOU race because item.findUnique followed by item.update can let concurrent
approvals both claim the same item. Update the approval flow in the claim status
handling logic to use a conditional write on tx.item, matching the existing
updateMany pattern used elsewhere, so the write only succeeds when itemId
matches and status is ItemStatus.stored, and handle the zero-row result as the
conflict case.
🧹 Nitpick comments (1)
backend/src/lib/logger.ts (1)

1-6: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Consider adding pino's built-in redact option for defense-in-depth.

Redaction currently relies solely on writeAuditLog manually choosing which fields to pass to logger.info (excluding details/ipAddress). If any future caller logs the full audit params or record directly, PII (e.g. ipAddress, details such as studentFullName, contactNumber) could leak into logs. Configuring pino's native redact paths on this shared logger would enforce redaction centrally rather than relying on every call site being careful.

♻️ Suggested addition
 export const logger = pino({
   level: process.env.LOG_LEVEL || 'info',
+  redact: {
+    paths: ['ipAddress', 'details', '*.ipAddress', '*.details'],
+    censor: '[REDACTED]',
+  },
 });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/src/lib/logger.ts` around lines 1 - 6, The shared logger in logger
should enforce central PII protection instead of relying only on writeAuditLog
to omit sensitive fields. Update the pino configuration in logger to use its
built-in redact option so fields like ipAddress and nested details values such
as studentFullName and contactNumber are always redacted even if future callers
log full audit params or records. Keep the change localized to the logger setup
so all consumers of logger inherit the safeguard automatically.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/README.md`:
- Around line 267-269: The API status table is inconsistent with the implemented
`PATCH /api/items/:itemId/status` endpoint because it still shows the route as
Planned even though the docs describe a finished handler. Update the summary
table entry for `router.patch('/items/:itemId/status', ...)` to match the actual
implementation state, likely changing the Status from Planned to Done so the
README and endpoint details stay aligned.

In `@backend/src/routes/claims.ts`:
- Around line 1052-1071: The audit entry in the claim notification flow is being
written inside the transaction through writeAuditLog(..., tx), which can log
claim_notification_sent before the notification is actually committed. Update
the claim status notification path in backend/src/routes/claims.ts so the audit
log happens only after the tx has successfully committed, or make writeAuditLog
skip logger.info() when a transaction client is passed. Use the
createClaimStatusNotificationInput, notification.create, and writeAuditLog call
sites to locate the change.

---

Outside diff comments:
In `@backend/src/routes/claims.ts`:
- Around line 1011-1032: The item status check and update inside the
prisma.$transaction block in claims.ts are vulnerable to a TOCTOU race because
item.findUnique followed by item.update can let concurrent approvals both claim
the same item. Update the approval flow in the claim status handling logic to
use a conditional write on tx.item, matching the existing updateMany pattern
used elsewhere, so the write only succeeds when itemId matches and status is
ItemStatus.stored, and handle the zero-row result as the conflict case.

---

Nitpick comments:
In `@backend/src/lib/logger.ts`:
- Around line 1-6: The shared logger in logger should enforce central PII
protection instead of relying only on writeAuditLog to omit sensitive fields.
Update the pino configuration in logger to use its built-in redact option so
fields like ipAddress and nested details values such as studentFullName and
contactNumber are always redacted even if future callers log full audit params
or records. Keep the change localized to the logger setup so all consumers of
logger inherit the safeguard automatically.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 178c57b3-108b-4d1a-814f-6e3c5f8afc78

📥 Commits

Reviewing files that changed from the base of the PR and between a15abc8 and f017631.

⛔ Files ignored due to path filters (1)
  • backend/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (6)
  • backend/README.md
  • backend/package.json
  • backend/src/lib/logger.ts
  • backend/src/routes/claims.ts
  • backend/src/routes/items.ts
  • backend/src/utils/auditLog.ts

Comment thread backend/README.md
Comment on lines +267 to +269
Allowed targets: `expired`, `disposed`. Transitions: `stored → expired|disposed`, `expired → disposed`. Blocked when item is `claimed` or `disposed`. Returns updated item detail; rejects active linked claims in the same transaction.

A daily cron job (`expireRetainedItems`) also transitions `stored → expired` when `retentionExpiryDate` has passed (skips items with an approved claim awaiting pickup). Security should use `disposed` to record physical disposal after expiry.
A daily cron job (`expireRetainedItems`) also transitions `stored → expired` when `retentionExpiryDate` has passed, with a defensive skip for stored items that already have an approved claim. Security should use `disposed` to record physical disposal after expiry.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Items table still marks this endpoint "Planned" while this section documents a fully implemented endpoint.

This section now documents detailed allowed targets, transitions, blocking rules, and cron interaction for PATCH /api/items/:itemId/status, but the summary table row for that same route (unchanged, Line 234) still lists its Status as Planned. The referenced router.patch('/items/:itemId/status', ...) handler (per supplied context) appears fully implemented with validation, transaction, and audit logging — the status label should likely be updated to Done to avoid misleading API consumers.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/README.md` around lines 267 - 269, The API status table is
inconsistent with the implemented `PATCH /api/items/:itemId/status` endpoint
because it still shows the route as Planned even though the docs describe a
finished handler. Update the summary table entry for
`router.patch('/items/:itemId/status', ...)` to match the actual implementation
state, likely changing the Status from Planned to Done so the README and
endpoint details stay aligned.

Comment on lines +1052 to +1071
const notification = await tx.notification.create({
data: createClaimStatusNotificationInput(nextClaim, status),
});

await writeAuditLog(
{
actorId: actor.userId,
action: 'claim_notification_sent',
entityType: 'notification',
entityId: notification.notificationId,
details: {
claimId: nextClaim.claimId,
recipientId: nextClaim.studentId,
claimStatus: status,
},
ipAddress: req.ip,
},
tx
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== claims route excerpt ==\n'
sed -n '1020,1085p' backend/src/routes/claims.ts

printf '\n== audit log utility ==\n'
sed -n '1,120p' backend/src/utils/auditLog.ts

Repository: 86unj/Foundit

Length of output: 3164


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== transaction surrounding the snippet ==\n'
sed -n '930,1095p' backend/src/routes/claims.ts

printf '\n== auditLog helper usages ==\n'
rg -n "writeAuditLog\\(" backend/src -g '!**/node_modules/**'

printf '\n== logger import and config in auditLog helper ==\n'
sed -n '1,120p' backend/src/lib/logger.ts

Repository: 86unj/Foundit

Length of output: 6369


Defer the audit log until after commit backend/src/routes/claims.ts:1052-1071
writeAuditLog(..., tx) emits logger.info() before the surrounding transaction commits, so a late commit failure can leave a claim_notification_sent log for a rolled-back notification. Move that log outside the transaction boundary or skip logging when a transaction client is passed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/src/routes/claims.ts` around lines 1052 - 1071, The audit entry in
the claim notification flow is being written inside the transaction through
writeAuditLog(..., tx), which can log claim_notification_sent before the
notification is actually committed. Update the claim status notification path in
backend/src/routes/claims.ts so the audit log happens only after the tx has
successfully committed, or make writeAuditLog skip logger.info() when a
transaction client is passed. Use the createClaimStatusNotificationInput,
notification.create, and writeAuditLog call sites to locate the change.

@humbeatbox
humbeatbox merged commit 88fdc93 into main Jul 6, 2026
7 checks passed
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