You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add a tamper-evident audit log for financial and security-sensitive actions. A platform that moves real money on Stellar and grants content entitlements needs an immutable, queryable record of who did what, when, from where — wallet connect/disconnect, payment initialize/submit/cancel, access grants, refunds (#20), payouts (#28), role changes (#20), and auth events. Today these actions leave only free-text logger.info lines in rotating Winston files: not queryable, not structured, not retained as records, and trivially lost on log rotation.
Current state
Security/financial actions are recorded only as log strings, e.g. logger.info(\Wallet connected for user ${userId}: ${publicKey}`) (src/controllers/stellar/walletController.js), logger.info(`Payment initialized: ${transaction._id} ...`)andlogger.info(`Payment successful: ${transactionId}, Stellar TX: ${result.hash}`) (src/controllers/stellar/paymentController.js). These go to Winston (src/config/logger.js`) with daily rotation — not to the database, not queryable, no actor/IP/outcome structure.
There is no AuditLog model and no audit collection anywhere in src/models/.
requestLogger (src/middlewares/security.js) logs every HTTP request line (method/url/status/ms/IP) but it's generic access logging, not a durable audit trail of business actions, and again only to Winston.
Sensitive mutations have no durable "before/after" trail: profile updates (userController.updateUser), wallet bind/unbind, and (incoming) role changes leave nothing an admin could later review for abuse or compliance.
Model (src/models/AuditLog.js): { action (enum), actor (ref User, nullable for anonymous), actorIp, actorUserAgent, targetType, targetId, status (success|failure), metadata (mixed, redacted), requestId, createdAt }. Index { actor, createdAt }, { action, createdAt }, { targetType, targetId }. Make it append-only: block updates/deletes at the model layer (pre-hooks throwing), and never expose a mutation route.
Audit service + helper (src/services/audit/auditService.js, recordAudit({...})): non-blocking (never fail the main request if the write fails — log and move on), with a redaction allowlist so secrets/PII (passwords, full wallet balances, tokens) are never persisted. Capture IP/UA from the request.
Instrument the sensitive paths (call the helper, don't rip out existing logger calls):
Retention note: document a retention/TTL policy decision (financial events likely retained long-term; consider a separate longer TTL than the 30-min Transaction pending TTL). Do not auto-expire financial audit rows by default.
Acceptance criteria
An append-only AuditLog model exists with the indexes above; attempts to update or delete an audit document are rejected at the model layer.
Wallet connect/disconnect, payment initialize/submit/cancel, and auth login/register/password-reset write structured audit rows capturing actor, IP, user-agent, target, and success/failure.
Audit writes never block or fail the primary request, and never persist secrets/PII (redaction allowlist enforced — a test asserts a password/token is absent from stored metadata).
GET /api/admin/audit is read-only, role-gated (401/403 for non-admins), and supports filtering by actor/action/target/date/status with pagination.
No endpoint allows mutating or deleting audit records.
Jest + supertest tests cover: an audited action producing a correct row, redaction of a sensitive field, admin-only access, and filter/pagination correctness.
Pointers
src/controllers/stellar/walletController.js and src/controllers/stellar/paymentController.js (existing logger.info sites to augment), src/controllers/authController.js (registerUser/loginUser/requestPasswordReset/resetPassword), src/controllers/userController.js (updateUser), src/middlewares/security.js (requestLogger for IP capture pattern), src/config/logger.js.
Medium — conceptually straightforward but requires careful append-only modeling, non-blocking instrumentation across many controllers, disciplined redaction, and a role-gated query API.
🏆 GrantFox OSS — Official Campaign | FWC26. Apply for this issue through the GrantFox campaign page. The maintainer assigns one contributor before work starts; unassigned PRs may not be reviewed. PRs target the dev branch. Quality bar: CI must stay green.
💬 Questions or need help? Reach the maintainers and other contributors on the DeenBridge Telegram: https://t.me/+nst9lXNj1wc4ZDE0
Summary
Add a tamper-evident audit log for financial and security-sensitive actions. A platform that moves real money on Stellar and grants content entitlements needs an immutable, queryable record of who did what, when, from where — wallet connect/disconnect, payment initialize/submit/cancel, access grants, refunds (#20), payouts (#28), role changes (#20), and auth events. Today these actions leave only free-text
logger.infolines in rotating Winston files: not queryable, not structured, not retained as records, and trivially lost on log rotation.Current state
logger.info(\Wallet connected for user ${userId}: ${publicKey}`)(src/controllers/stellar/walletController.js),logger.info(`Payment initialized: ${transaction._id} ...`)andlogger.info(`Payment successful: ${transactionId}, Stellar TX: ${result.hash}`)(src/controllers/stellar/paymentController.js). These go to Winston (src/config/logger.js`) with daily rotation — not to the database, not queryable, no actor/IP/outcome structure.AuditLogmodel and no audit collection anywhere insrc/models/.requestLogger(src/middlewares/security.js) logs every HTTP request line (method/url/status/ms/IP) but it's generic access logging, not a durable audit trail of business actions, and again only to Winston.userController.updateUser), wallet bind/unbind, and (incoming) role changes leave nothing an admin could later review for abuse or compliance.What to build
src/models/AuditLog.js):{ action (enum), actor (ref User, nullable for anonymous), actorIp, actorUserAgent, targetType, targetId, status (success|failure), metadata (mixed, redacted), requestId, createdAt }. Index{ actor, createdAt },{ action, createdAt },{ targetType, targetId }. Make it append-only: block updates/deletes at the model layer (pre-hooks throwing), and never expose a mutation route.src/services/audit/auditService.js,recordAudit({...})): non-blocking (never fail the main request if the write fails — log and move on), with a redaction allowlist so secrets/PII (passwords, full wallet balances, tokens) are never persisted. Capture IP/UA from the request.loggercalls):authController.js).walletController.js).paymentController.js).src/routes/admin/auditRoutes.js, mounted/api/admin/audit, role-gated per [Enhancement] Introduce role-based authorization and fix registration privilege escalation #20):GET /api/admin/audit?actor=&action=&targetType=&from=&to=&status=&page=&limit=— paginated, filterable, read-only. No create/update/delete endpoints.Transactionpending TTL). Do not auto-expire financial audit rows by default.Acceptance criteria
AuditLogmodel exists with the indexes above; attempts to update or delete an audit document are rejected at the model layer.GET /api/admin/auditis read-only, role-gated (401/403for non-admins), and supports filtering by actor/action/target/date/status with pagination.Pointers
src/controllers/stellar/walletController.jsandsrc/controllers/stellar/paymentController.js(existinglogger.infosites to augment),src/controllers/authController.js(registerUser/loginUser/requestPasswordReset/resetPassword),src/controllers/userController.js(updateUser),src/middlewares/security.js(requestLoggerfor IP capture pattern),src/config/logger.js.awaitin a way that can fail the response). Redact by allowlist, not denylist.User.rolehas noadminyet — coordinate the gate with [Enhancement] Introduce role-based authorization and fix registration privilege escalation #20. CI runs a real Mongo 7. PRs targetdev.Difficulty
Medium — conceptually straightforward but requires careful append-only modeling, non-blocking instrumentation across many controllers, disciplined redaction, and a role-gated query API.
🏆 GrantFox OSS — Official Campaign | FWC26. Apply for this issue through the GrantFox campaign page. The maintainer assigns one contributor before work starts; unassigned PRs may not be reviewed. PRs target the
devbranch. Quality bar: CI must stay green.💬 Questions or need help? Reach the maintainers and other contributors on the DeenBridge Telegram: https://t.me/+nst9lXNj1wc4ZDE0