Skip to content

Commit 8749668

Browse files
authored
feat: add ability for users to enable data message logging (#95)
* feat: add ability for users to enable data message logging
1 parent 74fe3bb commit 8749668

File tree

13 files changed

+455
-9
lines changed

13 files changed

+455
-9
lines changed

README.md

+8
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,14 @@ We welcome community contributions and pull requests. See [CONTRIBUTING.md](./CO
311311
# Security
312312
The AWS Message Processing Framework for .NET relies on the [AWS SDK for .NET](https://github.com/aws/aws-sdk-net) for communicating with AWS. Refer to the [security section](https://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/security.html) in the [AWS SDK for .NET Developer Guide](https://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/welcome.html) for more information.
313313

314+
The framework does not log data messages sent by the user for security purposes. If users want to enable this functionality for debugging purposes, you need to call `EnableDataMessageLogging()` in the AWS Message Bus as follows:
315+
```csharp
316+
builder.Services.AddAWSMessageBus(bus =>
317+
{
318+
builder.EnableDataMessageLogging();
319+
});
320+
```
321+
314322
If you discover a potential security issue, refer to the [security policy](https://github.com/awslabs/aws-dotnet-messaging/security/policy) for reporting information.
315323

316324
# Additional Resources

sampleapps/PublisherAPI/Program.cs

+3
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@
3232
PropertyNamingPolicy= JsonNamingPolicy.CamelCase,
3333
};
3434
});
35+
36+
// Logging data messages is disabled by default to protect sensitive user data. If you want this enabled, uncomment the line below.
37+
// builder.EnableDataMessageLogging();
3538
});
3639
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
3740
builder.Services.AddEndpointsApiExplorer();

sampleapps/PublisherAPI/appsettings.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
"EndpointID": "default"
4141
}
4242
}
43-
]
43+
],
44+
"LogMessageContent": false
4445
}
4546
}

sampleapps/SubscriberService/Program.cs

+3
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ await Host.CreateDefaultBuilder(args)
3939
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
4040
};
4141
});
42+
43+
// Logging data messages is disabled by default to protect sensitive user data. If you want this enabled, uncomment the line below.
44+
// builder.EnableDataMessageLogging();
4245
})
4346
.AddOpenTelemetry()
4447
.ConfigureResource(resource => resource.AddService("SubscriberService"))

src/AWS.Messaging/Configuration/IMessageBusBuilder.cs

+8-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ IMessageBusBuilder AddMessageHandler<THandler, TMessage>(string? messageTypeIden
7676
/// </summary>
7777
/// <param name="suffix">The suffix to append to the message source.</param>
7878
IMessageBusBuilder AddMessageSourceSuffix(string suffix);
79-
79+
8080
/// <summary>
8181
/// Retrieve the Message Processing Framework section from <see cref="IConfiguration"/>
8282
/// and apply the Message bus configuration based on that section.
@@ -90,4 +90,11 @@ IMessageBusBuilder AddMessageHandler<THandler, TMessage>(string? messageTypeIden
9090
/// <param name="serviceDescriptor">The service descriptor for the added service.</param>
9191
/// <returns></returns>
9292
IMessageBusBuilder AddAdditionalService(ServiceDescriptor serviceDescriptor);
93+
94+
/// <summary>
95+
/// Enables the visibility of data messages in the logging framework, exception handling and other areas.
96+
/// If this is enabled, messages sent by this framework will be visible in plain text across the framework's components.
97+
/// This means any sensitive user data sent by this framework will be visible in logs, any exceptions thrown and others.
98+
/// </summary>
99+
IMessageBusBuilder EnableMessageContentLogging();
93100
}

src/AWS.Messaging/Configuration/IMessageConfiguration.cs

+7
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,11 @@ public interface IMessageConfiguration
6767
/// computed by the framework in the absence of a user-defined one.
6868
/// </summary>
6969
string? SourceSuffix { get; set; }
70+
71+
/// <summary>
72+
/// Controls the visibility of data messages in the logging framework, exception handling and other areas.
73+
/// If this is enabled, messages sent by this framework will be visible in plain text across the framework's components.
74+
/// This means any sensitive user data sent by this framework will be visible in logs, any exceptions thrown and others.
75+
/// </summary>
76+
bool LogMessageContent { get; set; }
7077
}

src/AWS.Messaging/Configuration/Internal/ApplicationSettings.cs

+7
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,13 @@ public class ApplicationSettings
3939
/// </summary>
4040
public ICollection<SQSPoller> SQSPollers { get; set; } = default!;
4141

42+
/// <summary>
43+
/// Controls the visibility of data messages in the logging framework, exception handling and other areas.
44+
/// If this is enabled, messages sent by this framework will be visible in plain text across the framework's components.
45+
/// This means any sensitive user data sent by this framework will be visible in logs, any exceptions thrown and others.
46+
/// </summary>
47+
public bool? LogMessageContent { get; set; } = default;
48+
4249
/// <summary>
4350
/// Represents an SQS publisher configuration.
4451
/// </summary>

src/AWS.Messaging/Configuration/MessageBusBuilder.cs

+24-2
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ public IMessageBusBuilder AddSQSPoller(string queueUrl, Action<SQSMessagePollerO
125125
VisibilityTimeoutExtensionHeartbeatInterval = sqsMessagePollerOptions.VisibilityTimeoutExtensionHeartbeatInterval,
126126
WaitTimeSeconds = sqsMessagePollerOptions.WaitTimeSeconds,
127127
IsSQSExceptionFatal = sqsMessagePollerOptions.IsSQSExceptionFatal
128-
128+
129129
};
130130

131131
_messageConfiguration.MessagePollerConfigurations.Add(sqsMessagePollerConfiguration);
@@ -159,7 +159,7 @@ public IMessageBusBuilder AddMessageSourceSuffix(string suffix)
159159
_messageConfiguration.SourceSuffix = suffix;
160160
return this;
161161
}
162-
162+
163163
/// <inheritdoc/>
164164
public IMessageBusBuilder LoadConfigurationFromSettings(IConfiguration configuration)
165165
{
@@ -239,6 +239,9 @@ public IMessageBusBuilder LoadConfigurationFromSettings(IConfiguration configura
239239
}
240240
}
241241

242+
if (settings.LogMessageContent != null)
243+
_messageConfiguration.LogMessageContent = settings.LogMessageContent ?? false;
244+
242245
return this;
243246
}
244247

@@ -261,8 +264,17 @@ public IMessageBusBuilder AddAdditionalService(ServiceDescriptor serviceDescript
261264
return this;
262265
}
263266

267+
/// <inheritdoc/>
268+
public IMessageBusBuilder EnableMessageContentLogging()
269+
{
270+
_messageConfiguration.LogMessageContent = true;
271+
return this;
272+
}
273+
264274
internal void Build(IServiceCollection services)
265275
{
276+
LoadConfigurationFromEnvironment();
277+
266278
// Make sure there is at least the default null implementation of the logger to injected so that
267279
// the DI constructors can be satisfied.
268280
services.TryAdd(ServiceDescriptor.Singleton<ILoggerFactory, NullLoggerFactory>());
@@ -330,4 +342,14 @@ internal void Build(IServiceCollection services)
330342
services.Add(service);
331343
}
332344
}
345+
346+
/// <summary>
347+
/// Retrieve Message Processing Framework configuration from environment variables.
348+
/// </summary>
349+
private void LoadConfigurationFromEnvironment()
350+
{
351+
var logMessageContentEnvVar = Environment.GetEnvironmentVariable("AWSMESSAGING_LOGMESSAGECONTENT");
352+
if (!string.IsNullOrEmpty(logMessageContentEnvVar) && bool.TryParse(logMessageContentEnvVar, out var logMessageContent))
353+
_messageConfiguration.LogMessageContent = logMessageContent;
354+
}
333355
}

src/AWS.Messaging/Configuration/MessageConfiguration.cs

+4-1
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,7 @@ public class MessageConfiguration : IMessageConfiguration
5151

5252
/// <inheritdoc/>
5353
public string? SourceSuffix { get; set; }
54-
}
54+
55+
/// <inheritdoc/>
56+
public bool LogMessageContent { get; set; }
57+
}

src/AWS.Messaging/Serialization/EnvelopeSerializer.cs

+19-2
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,21 @@ public async ValueTask<string> SerializeAsync<T>(MessageEnvelope<T> envelope)
109109
var jsonString = blob.ToJsonString();
110110
var serializedMessage = await InvokePostSerializationCallback(jsonString);
111111

112-
_logger.LogTrace("Serialized the MessageEnvelope object as the following raw string:\n{SerializedMessage}", serializedMessage);
112+
if (_messageConfiguration.LogMessageContent)
113+
{
114+
_logger.LogTrace("Serialized the MessageEnvelope object as the following raw string:\n{SerializedMessage}", serializedMessage);
115+
}
116+
else
117+
{
118+
_logger.LogTrace("Serialized the MessageEnvelope object to a raw string");
119+
}
113120
return serializedMessage;
114121
}
122+
catch (JsonException) when (!_messageConfiguration.LogMessageContent)
123+
{
124+
_logger.LogError("Failed to serialize the MessageEnvelope into a raw string");
125+
throw new FailedToSerializeMessageEnvelopeException("Failed to serialize the MessageEnvelope into a raw string");
126+
}
115127
catch (Exception ex)
116128
{
117129
_logger.LogError(ex, "Failed to serialize the MessageEnvelope into a raw string");
@@ -142,7 +154,7 @@ public async ValueTask<ConvertToEnvelopeResult> ConvertToEnvelopeAsync(Message s
142154

143155
if (Activator.CreateInstance(messageEnvelopeType) is not MessageEnvelope finalMessageEnvelope)
144156
{
145-
_logger.LogError("Failed to create a messageEnvelope of type '{MessageEnvelopeType}'", messageEnvelopeType.FullName);
157+
_logger.LogError($"Failed to create a {nameof(MessageEnvelope)} of type '{{MessageEnvelopeType}}'", messageEnvelopeType.FullName);
146158
throw new InvalidOperationException($"Failed to create a {nameof(MessageEnvelope)} of type '{messageEnvelopeType.FullName}'");
147159
}
148160

@@ -163,6 +175,11 @@ public async ValueTask<ConvertToEnvelopeResult> ConvertToEnvelopeAsync(Message s
163175
_logger.LogTrace("Created a generic {MessageEnvelopeName} of type '{MessageEnvelopeType}'", nameof(MessageEnvelope), result.Envelope.GetType());
164176
return result;
165177
}
178+
catch (JsonException) when (!_messageConfiguration.LogMessageContent)
179+
{
180+
_logger.LogError("Failed to create a {MessageEnvelopeName}", nameof(MessageEnvelope));
181+
throw new FailedToCreateMessageEnvelopeException($"Failed to create {nameof(MessageEnvelope)}");
182+
}
166183
catch (Exception ex)
167184
{
168185
_logger.LogError(ex, "Failed to create a {MessageEnvelopeName}", nameof(MessageEnvelope));

src/AWS.Messaging/Serialization/MessageSerializer.cs

+28-2
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,22 @@ public object Deserialize(string message, Type deserializedType)
2929
try
3030
{
3131
var jsonSerializerOptions = _messageConfiguration.SerializationOptions.SystemTextJsonOptions;
32-
_logger.LogTrace("Deserializing the following message into type '{DeserializedType}':\n{Message}", deserializedType, message);
32+
if (_messageConfiguration.LogMessageContent)
33+
{
34+
_logger.LogTrace("Deserializing the following message into type '{DeserializedType}':\n{Message}", deserializedType, message);
35+
}
36+
else
37+
{
38+
_logger.LogTrace("Deserializing the following message into type '{DeserializedType}'", deserializedType);
39+
}
40+
3341
return JsonSerializer.Deserialize(message, deserializedType, jsonSerializerOptions) ?? throw new JsonException("The deserialized application message is null.");
3442
}
43+
catch (JsonException) when (!_messageConfiguration.LogMessageContent)
44+
{
45+
_logger.LogError("Failed to deserialize application message into an instance of {DeserializedType}.", deserializedType);
46+
throw new FailedToDeserializeApplicationMessageException($"Failed to deserialize application message into an instance of {deserializedType}.");
47+
}
3548
catch (Exception ex)
3649
{
3750
_logger.LogError(ex, "Failed to deserialize application message into an instance of {DeserializedType}.", deserializedType);
@@ -47,9 +60,22 @@ public string Serialize(object message)
4760
{
4861
var jsonSerializerOptions = _messageConfiguration.SerializationOptions.SystemTextJsonOptions;
4962
var jsonString = JsonSerializer.Serialize(message, jsonSerializerOptions);
50-
_logger.LogTrace("Serialized the message object as the following raw string:\n{JsonString}", jsonString);
63+
if (_messageConfiguration.LogMessageContent)
64+
{
65+
_logger.LogTrace("Serialized the message object as the following raw string:\n{JsonString}", jsonString);
66+
}
67+
else
68+
{
69+
_logger.LogTrace("Serialized the message object to a raw string with a content length of {ContentLength}.", jsonString.Length);
70+
}
71+
5172
return jsonString;
5273
}
74+
catch (JsonException) when (!_messageConfiguration.LogMessageContent)
75+
{
76+
_logger.LogError("Failed to serialize application message into a string");
77+
throw new FailedToSerializeApplicationMessageException("Failed to serialize application message into a string");
78+
}
5379
catch (Exception ex)
5480
{
5581
_logger.LogError(ex, "Failed to serialize application message into a string");

0 commit comments

Comments
 (0)