Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion system/services/email/EmailLoggingService.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -736,10 +736,14 @@ component {
) {
var fieldsToAddFromExtraData = [ "link", "code", "reason", "link_title", "link_body" ];
var extra = StructCopy( arguments.extraData );
var userIp = arguments.userIp;
if ( isPrivateIp( userIp ) ) {
userIp = "";
}
var data = {
message = arguments.messageId
, activity_type = arguments.activity
, user_ip = arguments.userIp
, user_ip = userIp
, user_agent = arguments.userAgent
, datecreated = arguments.eventDate
};
Expand Down Expand Up @@ -1147,4 +1151,29 @@ component {
_emailStatsService = arguments.emailStatsService;
}

/**
* Checks if the given IP address is a private, loopback, or local IP address.
*
* @ipAddress.hint IP address to check
*/
public boolean function isPrivateIp( required string ipAddress ) {
var ip = Trim( arguments.ipAddress );
if ( ip == "::1" || ip == "localhost" ) {
return true;
}
// Match IPv4 private subnets (10.x, 127.x, 169.254.x, 192.168.x)
if ( ReFindNoCase( "^(10\.|127\.|169\.254\.|192\.168\.)", ip ) ) {
return true;
}
// Match IPv4 private subnet (172.16.x - 172.31.x)
if ( ReFindNoCase( "^172\.(1[6-9]|2[0-9]|3[0-1])\.", ip ) ) {
return true;
}
// Match IPv6 private/link-local ranges (fc00::/7, fe80::/10)
if ( ReFindNoCase( "^(fe[89ab][0-9a-f]:|fc[0-9a-f]{2}:|fd[0-9a-f]{2}:)", ip ) ) {
return true;
}
return false;
}

}
4 changes: 3 additions & 1 deletion system/views/admin/emailcenter/logs/_logActivity.cfm
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
<cfparam name="args.userAgent" default="" />
<cfparam name="args.showAuditTrail" default=true />

<cfset emailLoggingService = getModel( "emailLoggingService" ) />

<cfoutput>
<div class="timeline-item clearfix" data-date="#args.logDate#">
<div class="timeline-info">
Expand Down Expand Up @@ -36,7 +38,7 @@
<cfif Len( Trim( args.ipAddress & args.userAgent ) ) && args.showAuditTrail>
<div class="widget-header widget-header-small">
<span class="widget-toolbar no-border light-grey">
<cfif Len( Trim( args.ipAddress ) )>
<cfif Len( Trim( args.ipAddress ) ) && !emailLoggingService.isPrivateIp( args.ipAddress )>
<strong>#translateResource( 'cms:audittrail.item.ip' )#:</strong> #args.ipAddress#
</cfif>
<cfif Len( Trim( args.userAgent ) )>
Expand Down
29 changes: 26 additions & 3 deletions tests/unit/api/email/EmailLoggingServiceTest.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,15 @@ component extends="resources.HelperObjects.PresideBddTestCase" {
messageId = messageId
, activity = activity
, extraData = extraData
, userIp = "8.8.8.8"
);

expect( mockLogActivityDao.$callLog().insertData.len() ).toBe( 1 );
expect( mockLogActivityDao.$callLog().insertData[ 1 ] ).toBe( [ {
message = messageId
, activity_type = activity
, extra_data = SerializeJson( extraData )
, user_ip = cgi.remote_addr
, user_ip = "8.8.8.8"
, user_agent = cgi.http_user_agent
, datecreated = Now()
} ]);
Expand All @@ -112,14 +113,15 @@ component extends="resources.HelperObjects.PresideBddTestCase" {
messageId = messageId
, activity = activity
, extraData = extraData
, userIp = "8.8.8.8"
);

expect( mockLogActivityDao.$callLog().insertData.len() ).toBe( 1 );
expect( mockLogActivityDao.$callLog().insertData[ 1 ] ).toBe( [ {
message = messageId
, activity_type = activity
, extra_data = SerializeJson( expectedData )
, user_ip = cgi.remote_addr
, user_ip = "8.8.8.8"
, user_agent = cgi.http_user_agent
, link = extraData.link
, code = extraData.code
Expand Down Expand Up @@ -147,21 +149,42 @@ component extends="resources.HelperObjects.PresideBddTestCase" {
messageId = messageId
, activity = activity
, extraData = extraData
, userIp = "8.8.8.8"
);

expect( service.$callLog().$announceInterception.len() ).toBe( 1 );
expect( service.$callLog().$announceInterception[ 1 ] ).toBe( [ "onEmailWhatever", {
message = messageId
, activity_type = activity
, extra_data = SerializeJson( expectedData )
, user_ip = cgi.remote_addr
, user_ip = "8.8.8.8"
, user_agent = cgi.http_user_agent
, link = extraData.link
, code = extraData.code
, reason = extraData.reason
, datecreated = Now()
} ] );
} );

it( "should blank out private or loopback IP addresses when recording activity", function(){
var service = _getService();
var messageId = CreateUUId();
var activity = "blah";
var extraData = { blah=CreateUUId(), test=Now() };

mockLogActivityDao.$( "insertData", CreateUUId() );
_setupMockMessageQuery( messageId );

service.recordActivity(
messageId = messageId
, activity = activity
, extraData = extraData
, userIp = "127.0.0.1"
);

expect( mockLogActivityDao.$callLog().insertData.len() ).toBe( 1 );
expect( mockLogActivityDao.$callLog().insertData[ 1 ][ 1 ].user_ip ).toBe( "" );
} );
} );

describe( "markAsSent()", function(){
Expand Down
Loading