Fix remote address extraction in audit event origin string#3992
Conversation
238743d to
5013c7a
Compare
The audit event origin builder cast Authentication.getDetails() to String
unconditionally to parse a remoteAddress out of a JSON blob. When details
was not a String — a common case for UaaAuthenticationDetails and
OAuth2AuthenticationDetails, which expose the remote address directly —
the cast raised ClassCastException, which the broad catch swallowed by
appending the raw Object.toString() to the audit log. This leaked
internal state (map contents, request parameters, token fragments,
session markers) into the log line and made the origin's shape depend
on which details type happened to be attached to the Authentication.
For example, an OAuth2AuthenticationDetails instance would have its full
toString appended, producing a log line like:
Audit: GroupModifiedEvent ('{"role_collection":"evenRC1771046947","op":"add_saml_attribute","saml_entity":"iot-idp-1771046947","attribute":"Groups","value":"even"}'): principal=evenRC1771046947, origin=[client=admin, details=(remoteAddress=172.18.0.1, sessionId=<SESSION>, tokenType=Bearer, tokenValue=<TOKEN>, sub=admin, iss=http://localhost:8080/uaa/oauth/token)], identityZoneId=[uaa]
where the remoteAddress/sessionId/tokenType/tokenValue fragment came from
the details object's toString, then sub/iss were appended by the token
inspector — a shape that mixed two unrelated sources into one line.
Replace the cast-and-catch with a pattern-matching switch that reads the
remote address from each known details type: UaaAuthenticationDetails,
OAuth2AuthenticationDetails, a Map (used by
ResourceOwnerPasswordTokenGranter — currently has no remoteAddress key,
but supported for forward compatibility), or a String parsed as JSON.
Non-JSON strings, maps without a remoteAddress key, and null-valued
fields fall through safely. Details types without a known accessor
produce no remoteAddress line rather than dumping their contents.
Add unit tests in AbstractUaaEventTest for each supported details type,
for the non-JSON String and null-remoteAddress regression cases, for the
Map paths (with and without a remoteAddress key), and for the
client+user origin shape.
Update AuditCheckMockMvcTests to reflect that audit origins no longer
carry the sessionId=<SESSION> marker: the marker was only ever emitted
via the buggy toString-leak path, carried no information (its value was
a hardcoded literal), and disappears now that the leak is fixed. Drop
the sessionId=<SESSION> assertions and collapse the
assertLogMessageWithSession / assertLogMessageWithoutSession helpers,
which are now equivalent, into a single assertLogMessage helper.
5013c7a to
e9543b5
Compare
|
@tack-sap, do you remember why Context: I'm implementing custom audit logging on our side and used cf-uaa as reference. AI noticed a |
There was a problem hiding this comment.
Pull request overview
Fixes audit event origin construction to safely extract remoteAddress from Authentication.getDetails() across multiple runtime shapes, avoiding ClassCastException and preventing audit-log contamination from details.toString() dumps.
Changes:
- Replaces cast-and-catch parsing with type-based extraction for
remoteAddress(including JSON-string andMapsupport). - Adds/updates unit tests to ensure details content is not leaked into audit origin strings.
- Updates MockMvc audit tests to stop asserting the legacy
sessionId=<SESSION>marker.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
server/src/main/java/org/cloudfoundry/identity/uaa/audit/event/AbstractUaaEvent.java |
Introduces type-based remoteAddress extraction and removes reliance on details.toString() fallbacks. |
server/src/test/java/org/cloudfoundry/identity/uaa/audit/event/AbstractUaaEventTest.java |
Adds coverage for new origin-shaping behaviors across multiple details types. |
uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/audit/AuditCheckMockMvcTests.java |
Updates audit expectations to match the new origin format (removing sessionId=<SESSION> assumptions). |
c55193d to
5a315c9
Compare
|
@gdgenchev we will release soon, so if you want cleanup your PR and then we can take it with release |
…ion events Replace details.toString() in AbstractUaaAuthenticationEvent.getOrigin() with an explicit implementation that emits only remoteAddress and clientId, so the session identifier is no longer included even in masked form.
4f15fdd to
f5b6cd0
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
server/src/main/java/org/cloudfoundry/identity/uaa/audit/event/AbstractUaaEvent.java:148
- Logging a WARN for every unhandled
Authentication.detailstype ingetOrigincan generate a lot of noise in production (audit origin is built for many events, and new auth mechanisms may introduce other detail types). Consider lowering this to DEBUG or removing it to avoid log spam.
default -> {
logger.warn("Unhandled Authentication.details type in audit origin: {}", details.getClass().getName());
yield Optional.empty();
}
8531798 to
7d89e23
Compare
Summary
Fixes an issue in the audit event origin builder where
Authentication.getDetails()was unconditionally cast toStringto extract theremoteAddress.When
detailswas an object type (e.g.,UaaAuthenticationDetailsorOAuth2AuthenticationDetails), this cast raised aClassCastException. The exception was swallowed by a broadcatchblock that appendeddetails.toString()directly to the audit log. This resulted in a possible issue:Data Leakage / Contamination: Internal state (such as token values, request parameters, and map contents) was leaked into audit logs, often mixing unrelated sources into a single line.
Example (Before)
For an
OAuth2AuthenticationDetailsinstance, the log line improperly included the fulltoString()dump along with appended token inspector fields:The
remoteAddress/sessionId/tokenType/tokenValuefragment came from the details object'stoString, thensub/isswere appended by the token inspector — a shape that mixed two unrelated sources into one line.Changes
Replaced the aggressive cast-and-catch approach with pattern-matching
switchlogic that reads theremoteAddressfield safely depending on the runtime type ofdetails:UaaAuthenticationDetails,OAuth2AuthenticationDetails,WebAuthenticationDetails: Directly invokes their respective accessor methods (getOrigin()/getRemoteAddress()).Map: Reads theremoteAddresskey (supportsResourceOwnerPasswordTokenGranterand ensures forward compatibility).String: Parsed as JSON to look forremoteAddress. Non-JSON strings fall through safely.nullfields gracefully fall through without emitting a field, preventing arbitrary state dumps.Side effect:
sessionId=<SESSION>marker is droppedThe
sessionId=<SESSION>marker previously present in audit origins only appeared via the buggytoString()-dump path. Its value was a hardcoded literal — not a session identifier — so it carried no correlation or lookup signal.type=UaaAuthenticationDetailsin the same origin line already conveys "session-shaped auth," making the marker redundant.AuditCheckMockMvcTestsis updated accordingly:sessionId=<SESSION>assertions are removed.assertLogMessageWithSessionandassertLogMessageWithoutSessioncollapse into a singleassertLogMessagehelper (they are now equivalent).Test Coverage
Added unit tests in
AbstractUaaEventTestcovering:UaaAuthenticationDetailsandOAuth2AuthenticationDetails— remoteAddress extraction via typed accessors.remoteAddresskey paths, verifying map contents are not leaked.nullremoteAddress.client + userorigin shape in audit output.