Skip to content

Add pino logger with redaction for Stellar secret keys (issue #442)#482

Merged
ritik4ever merged 2 commits into
ritik4ever:mainfrom
KCEE0901:issue-442-redact-secrets
May 28, 2026
Merged

Add pino logger with redaction for Stellar secret keys (issue #442)#482
ritik4ever merged 2 commits into
ritik4ever:mainfrom
KCEE0901:issue-442-redact-secrets

Conversation

@KCEE0901
Copy link
Copy Markdown

@KCEE0901 KCEE0901 commented May 26, 2026

Closes #442

Checklist

  • I kept the change focused on the related issue.
  • I added or updated tests where useful.
  • I updated documentation where behavior changed.
  • I verified the app still builds or explained why verification was skipped.

Summary by CodeRabbit

  • Documentation

    • Added "Logging and Secret Redaction" section to security guidelines documenting automatic redaction of sensitive values in server logs.
  • New Features

    • Implemented automatic redaction of sensitive data (secret keys, tokens, private keys, and Stellar seed strings) in application logs.
  • Tests

    • Added test coverage for secret redaction functionality.

Review Change Stack

@vercel
Copy link
Copy Markdown

vercel Bot commented May 26, 2026

@KCEE0901 is attempting to deploy a commit to the ritik4ever's projects Team on Vercel.

A member of the Team first needs to authorize it.

@drips-wave
Copy link
Copy Markdown

drips-wave Bot commented May 26, 2026

@KCEE0901 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 26, 2026

Warning

Review limit reached

@ritik4ever, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 55 minutes and 11 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 06eb7987-aab8-422e-9ac7-aac5a5dd5aec

📥 Commits

Reviewing files that changed from the base of the PR and between b40e539 and 25b3c91.

📒 Files selected for processing (1)
  • backend/package.json
📝 Walkthrough

Walkthrough

This PR implements structured logging with automatic redaction of Stellar secret keys. It adds pino as a logger with both field-path masking and regex pattern detection, integrates it into request logging, and documents the redaction behavior in the security policy.

Changes

Logging and Secret Redaction

Layer / File(s) Summary
Secret redaction logic and verification
backend/src/logger.ts, backend/src/logger.test.ts
Implements STELLAR_SECRET_REGEX pattern to detect Stellar secrets, creates redactValue and redactObject functions to recursively mask secrets in nested structures, and verifies through Jest tests that secrets are replaced with [REDACTED].
Logger configuration with pino and redaction
backend/package.json, backend/src/logger.ts
Adds pino (^8.11.0) as a runtime dependency, then initializes a logger with environment-driven log level, redact.paths censoring for secretKey, privateKey, and seed fields, and a formatters.log hook that runs redactObject on all logged payloads.
Request logger integration
backend/src/middleware/requestLogger.ts
Updates requestLogger middleware to import and use the structured logger, replacing prior environment-dependent console.log branching with a single logger.info() call that includes request method, URL, status, duration, and request ID.
Security documentation
SECURITY.md
Adds a "Logging and Secret Redaction" section describing structured field redaction, regex-based redaction for Stellar secret patterns, and guidance to report any logs containing unredacted secrets via the private vulnerability process.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 A logger hops in, secrets tucked away,
Pino masks the keys in logs today,
With paths and regex both on guard,
Stellar seeds won't be logged hard,
Redacted safe, come what may! 🔐

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: adding pino logger with redaction for Stellar secret keys, and references the related issue #442.
Linked Issues check ✅ Passed All acceptance criteria from issue #442 are met: pino redact paths configured for secretKey/privateKey/seed, regex redaction for Stellar secret pattern implemented, unit test added, and SECURITY.md documentation provided.
Out of Scope Changes check ✅ Passed All changes are directly related to issue #442: logger configuration, redaction utilities, tests, documentation, and middleware integration for logging.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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/src/logger.test.ts`:
- Around line 3-18: Tests only exercise redactObject directly but don't assert
the logger actually emits redacted lines nor check the seed field; update the
"logger redaction" test to call the real logging path (use the module's logger
or logging function that wraps redactObject) and capture emitted log output
(e.g., spy or capture console/process logs) then assert the logged message
string contains "[REDACTED]" for each secret occurrence; also add an explicit
assertion that a "seed" field (e.g., obj.seed or similar key) is redacted in
both the object result from redactObject and in the emitted log output. Ensure
you reference redactObject and the exported logger/logging function used by the
code under test when adding the integration assertion.

In `@backend/src/middleware/requestLogger.ts`:
- Line 35: The current logger.info call in requestLogger.ts embeds
req.originalUrl into the free-form message which can leak sensitive query data;
change the call that references logger.info(logEntry, `${req.method}
${req.originalUrl} ${res.statusCode} - ${duration}ms | id=${requestId}`) so the
message text is static (e.g., "Request completed") and move dynamic values into
structured fields instead, using req.path (not req.originalUrl) for the URL, and
include method, res.statusCode, duration and requestId as separate properties on
the log entry so sensitive query params are not written into the free-form
message.
🪄 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

Run ID: 1fc1dee2-a070-4c9e-9f88-9ffc02368a1b

📥 Commits

Reviewing files that changed from the base of the PR and between b6e4bea and b40e539.

📒 Files selected for processing (5)
  • SECURITY.md
  • backend/package.json
  • backend/src/logger.test.ts
  • backend/src/logger.ts
  • backend/src/middleware/requestLogger.ts

Comment on lines +3 to +18
describe("logger redaction", () => {
test("redacts stellar secret keys in objects and strings", () => {
const secret = "S" + "A".repeat(55);
const obj = {
nested: { secretKey: secret, other: "ok" },
token: secret,
arr: [secret, { privateKey: secret }],
} as const;

const redacted = redactObject(obj as any);

expect(redacted.nested.secretKey).toBe("[REDACTED]");
expect(redacted.token).toBe("[REDACTED]");
expect(redacted.arr[0]).toBe("[REDACTED]");
expect(redacted.arr[1].privateKey).toBe("[REDACTED]");
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add an integration-style assertion against actual logger output.

Current coverage only tests redactObject; it does not verify emitted log lines are redacted, which is part of the acceptance criteria. Also add an explicit seed field assertion to cover all required paths.

Suggested test expansion
 import { redactObject } from "./logger";
+import { logger } from "./logger";

 describe("logger redaction", () => {
   test("redacts stellar secret keys in objects and strings", () => {
@@
-    const obj = {
+    const obj = {
       nested: { secretKey: secret, other: "ok" },
+      wallet: { seed: secret },
       token: secret,
       arr: [secret, { privateKey: secret }],
     } as const;
@@
     expect(redacted.nested.secretKey).toBe("[REDACTED]");
+    expect(redacted.wallet.seed).toBe("[REDACTED]");
     expect(redacted.token).toBe("[REDACTED]");
     expect(redacted.arr[0]).toBe("[REDACTED]");
     expect(redacted.arr[1].privateKey).toBe("[REDACTED]");
   });
+
+  test("does not emit raw stellar secrets in logger output", () => {
+    const secret = "S" + "A".repeat(55);
+    const spy = vi.spyOn(process.stdout, "write").mockImplementation(() => true);
+
+    logger.info({ secretKey: secret, token: secret }, "test log");
+
+    const written = spy.mock.calls.map(([chunk]) => String(chunk)).join("");
+    expect(written).not.toContain(secret);
+    expect(written).toContain("[REDACTED]");
+    spy.mockRestore();
+  });
 });
🤖 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/logger.test.ts` around lines 3 - 18, Tests only exercise
redactObject directly but don't assert the logger actually emits redacted lines
nor check the seed field; update the "logger redaction" test to call the real
logging path (use the module's logger or logging function that wraps
redactObject) and capture emitted log output (e.g., spy or capture
console/process logs) then assert the logged message string contains
"[REDACTED]" for each secret occurrence; also add an explicit assertion that a
"seed" field (e.g., obj.seed or similar key) is redacted in both the object
result from redactObject and in the emitted log output. Ensure you reference
redactObject and the exported logger/logging function used by the code under
test when adding the integration assertion.

);
}
// ✅ STEP 4: Use structured logger with redaction
logger.info(logEntry, `${req.method} ${req.originalUrl} ${res.statusCode} - ${duration}ms | id=${requestId}`);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid embedding raw URL data in the log message string.

Line 35 duplicates req.originalUrl into free-form text, which can leak sensitive query values. Keep message static and rely on structured fields (and preferably log req.path instead of full originalUrl when possible).

Safer logging pattern
-    logger.info(logEntry, `${req.method} ${req.originalUrl} ${res.statusCode} - ${duration}ms | id=${requestId}`);
+    logger.info(logEntry, "request_completed");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
logger.info(logEntry, `${req.method} ${req.originalUrl} ${res.statusCode} - ${duration}ms | id=${requestId}`);
logger.info(logEntry, "request_completed");
🤖 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/middleware/requestLogger.ts` at line 35, The current logger.info
call in requestLogger.ts embeds req.originalUrl into the free-form message which
can leak sensitive query data; change the call that references
logger.info(logEntry, `${req.method} ${req.originalUrl} ${res.statusCode} -
${duration}ms | id=${requestId}`) so the message text is static (e.g., "Request
completed") and move dynamic values into structured fields instead, using
req.path (not req.originalUrl) for the URL, and include method, res.statusCode,
duration and requestId as separate properties on the log entry so sensitive
query params are not written into the free-form message.

@ritik4ever ritik4ever merged commit 9368c68 into ritik4ever:main May 28, 2026
0 of 2 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.

Add pino log redaction for Stellar secret keys

2 participants