Skip to content

Harden logging: neutralize log injection + stop logging sensitive tokens (#464, #465)#480

Open
jorisjonkers-dev-agents[bot] wants to merge 2 commits into
mainfrom
fix/log-security-hardening
Open

Harden logging: neutralize log injection + stop logging sensitive tokens (#464, #465)#480
jorisjonkers-dev-agents[bot] wants to merge 2 commits into
mainfrom
fix/log-security-hardening

Conversation

@jorisjonkers-dev-agents

Copy link
Copy Markdown
Contributor

Summary

Changes

New helper — shared/util/LogSanitizer.kt

String?.forLog() replaces [\r\n] with underscores and handles nulls. Applied consistently at every flagged site so the pattern is greppable.

EmailTrackingController (lines 53, 55, 57)

Drops token from all three log calls. The success path logs outbox.id only; the not-found and error paths omit any token reference. This closes both the java/sensitive-log alerts (#465) and the corresponding java/log-injection hits for this file (#464).

BrevoListAdapter (lines 51, 57, 60)

User-supplied list name wrapped in .forLog() at all three createList log calls.

MockContactAdapter (line 84)

Same name.forLog() fix in the mock adapter's createList log.

ForwardAuthController (line 66)

X-Forwarded-Host header value sanitized before the unknown-host warn log.

ExceptionLoggingResolver (line 41)

request.requestURI and ex.message both sanitized in the catch-all error log.

FileService (line 92)

multipart.originalFilename sanitized before the store info log.

Test plan

  • CodeQL run on this branch clears all 10 java/log-injection alerts and all 3 java/sensitive-log alerts.
  • Existing API unit and integration tests pass in CI (no behaviour change — only log arguments are touched).
  • No build warnings introduced.

Closes #464
Closes #465

@jorisjonkers-dev-agents jorisjonkers-dev-agents Bot added the bug Something isn't working label Jul 17, 2026
val fullPath = rootLocation.resolve(path).normalize()

log.info("Storing {} at {}", multipart.originalFilename, fullPath)
log.info("Storing {} at {}", multipart.originalFilename.forLog(), fullPath)

override fun createList(name: String, folderName: String?): Long {
log.info("Creating Brevo list '{}'", name)
log.info("Creating Brevo list '{}'", name.forLog())
req.folderId = contributionPeriodsFolder
val response = contactsApi.createList(req)
log.info("Created Brevo list '{}' id={}", name, response.id)
log.info("Created Brevo list '{}' id={}", name.forLog(), response.id)
response.id
} catch (e: RestClientResponseException) {
log.error("Failed to create Brevo list '{}'", name, e)
log.error("Failed to create Brevo list '{}'", name.forLog(), e)
val listId = listIdSequence.getAndIncrement()
lists[listId] = MockList(listId = listId, listName = name, folderName = folderName)
log.info("Mock: Created list id={} name='{}'", listId, name)
log.info("Mock: Created list id={} name='{}'", listId, name.forLog())
// someone pointing forward-auth at us via Host injection) gets
// ADMIN-required. Warn so the operator notices.
log.warn("forward-auth: unknown host '{}' — defaulting to ADMIN", forwardedHost)
log.warn("forward-auth: unknown host '{}' — defaulting to ADMIN", forwardedHost.forLog())

@ExtraToast ExtraToast left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review comments made by CodeQL, dismiss and address appropriately, then re-requeested review

ExtraToast and others added 2 commits July 18, 2026 06:28
…ens (#464, #465)

Fixes CodeQL java/log-injection (×10) and java/sensitive-log (×3).

- Add LogSanitizer.forLog() extension in shared/util — strips CR/LF from
  user-controlled values before they reach log call sites.
- EmailTrackingController: drop raw tracking token from all three log
  statements; log outbox.id only when found, remove token from the
  not-found and error branches entirely. Resolves java/sensitive-log ×3
  and the corresponding java/log-injection hits for this file.
- BrevoListAdapter: sanitize user-supplied list name at all three createList
  log calls (lines 51, 57, 60).
- MockContactAdapter: sanitize list name at createList log call (line 84).
- ForwardAuthController: sanitize X-Forwarded-Host header value at the
  unknown-host warn (line 66).
- ExceptionLoggingResolver: sanitize request.requestURI and ex.message at
  the catch-all error log (line 41).
- FileService: sanitize multipart.originalFilename at the storeMultipart
  info log (line 92).

Closes #464
Closes #465

Co-Authored-By: Joris Jonkers <info@jorisjonkers.dev>
CodeQL (java/log-injection) flagged two contact-email interpolations the
initial hardening pass missed: the created-contact and deleted-contact log
lines embed the user-provided email verbatim. Wrap both with .forLog() to
strip CR/LF, matching the sanitizer already applied to the list-name and
filename call sites.

Co-Authored-By: Joris Jonkers <info@jorisjonkers.dev>
@jorisjonkers-dev-agents
jorisjonkers-dev-agents Bot force-pushed the fix/log-security-hardening branch from ae2bf75 to e9470cf Compare July 18, 2026 06:30
@jorisjonkers-dev-agents

Copy link
Copy Markdown
Contributor Author

Addressed the additional CodeQL java/log-injection findings from the review.

Sanitized (wrapped user-provided value with .forLog()):

  • MockContactAdapter.kt — created-contact log line: data.emaildata.email.forLog()
  • MockContactAdapter.kt — deleted-contact log line: removed.emailremoved.email.forLog()

These two contact-email interpolations were the remaining unsanitized user-influenced String values within the files this PR touches; the previously-flagged sites (FileService filename, BrevoListAdapter list name x3, ForwardAuthController forwarded host) were already sanitized in the initial pass and remain so. No log levels or messages were otherwise changed. Rebased on latest main.

Commit: e9470cf

@ExtraToast ExtraToast left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

There's still a lot of codeql notifications on this, fix them.

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

Labels

bug Something isn't working

Projects

None yet

2 participants