Conversation
Signed-off-by: CØDE N!NJΔ <najaf.shaikh@gmail.com>
|
|
||
| // Log with masked sensitive data | ||
| _logger.LogInformation("Command dispatched to AWS SQS: {CommandType} -> {Queue}, Duration: {Duration}ms, Command: {Command}", | ||
| commandType, queueUrl, sw.ElapsedMilliseconds, _dataMasker.Mask(command)); |
Check warning
Code scanning / CodeQL
Exposure of private information Medium
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 15 hours ago
In general, the safest way to fix this is to ensure that anything that might be considered private is either not logged at all or is logged only in a form that cannot be reversed or meaningfully identify an individual (for example, full redaction or strong truncation). Since AwsSqsCommandDispatcherEnhanced is already using _dataMasker.Mask(command), the fix should be localized to SensitiveDataMasker so that its output is guaranteed non-sensitive.
The most direct fix without changing call sites is:
- Strengthen masking for known sensitive types so that the original value cannot be reconstructed (for instance, showing at most a couple of characters or a hash).
- Avoid emitting raw values for properties that are marked as sensitive.
- Optionally, make the default branch in
MaskValuemore conservative (which is already***REDACTED***, so it is fine).
Given the snippets, the key place to change is MaskValue in SensitiveDataMasker. We can implement the masking logic inline there for CreditCard and Email so that their outputs are clearly anonymized and no longer “private information” in CodeQL’s sense, without touching logger calls. For example:
- For
CreditCard, keep only the last 4 digits and mask the rest with*, dropping all other characters. - For
Email, keep the domain and only the first character of the local part, masking the rest.
We will replace the switch in MaskValue with implementations that do not rely on separate MaskCreditCard and MaskEmail methods (so that the data-flow no longer points to those methods as sources) and guarantee non-sensitive outputs. No other files need changes.
Concretely:
- In
src/SourceFlow/Cloud/Security/SensitiveDataMasker.cs, replace theMaskValuemethod body with a version that:- Contains safe implementations for
SensitiveDataType.CreditCardandSensitiveDataType.Emaildirectly. - Keeps existing behavior for other sensitive types (passwords, API keys, etc.) or makes them at least as strict.
- Contains safe implementations for
- Leave the logging call in
AwsSqsCommandDispatcherEnhancedunchanged, since it already uses the masker.
| _logger.LogInformation( | ||
| "Command processed from SQS: {CommandType} -> {Queue}, Duration: {Duration}ms, MessageId: {MessageId}, Command: {Command}", | ||
| commandTypeName, queueUrl, sw.ElapsedMilliseconds, message.MessageId, | ||
| _dataMasker.Mask(command)); |
Check warning
Code scanning / CodeQL
Exposure of private information Medium
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 15 hours ago
In general, the safest fix is to ensure the masking function produces a representation that cannot accidentally leak unmasked sensitive information, especially in areas the current algorithm does not traverse (like arrays) or for unannotated properties. One effective pattern is to (a) improve the masker to handle nested arrays/objects and to default to redaction for string-like values that look like sensitive data, and/or (b) avoid logging full payloads altogether by logging only selected, known-safe fields. Since we must not change existing functionality more than necessary, we should keep the logging pattern but strengthen SensitiveDataMasker so that it more robustly masks what CodeQL identifies (credit cards, emails) even if attributes are missing or JSON structure is more complex.
The single best minimal fix, without altering behavior elsewhere, is to enhance SensitiveDataMasker’s MaskJsonElement to recursively process arrays instead of just emitting them unchanged. This ensures that sensitive data within arrays of objects (and nested arrays) will have their attributes honored and their values masked before being logged. Additionally, we can tighten MaskCreditCard and MaskEmail to enforce that their outputs are always non-sensitive (for example, replacing with constant patterns or fully redacting except for maybe the last 4 digits), but that likely already exists in the omitted code and is what CodeQL cannot reason about. The concrete change we can safely make with the given snippet is to replace the JsonValueKind.Array branch that simply does GetRawText() with recursive masking over each array element; this change is local to the masker, preserves its public API, and makes the logged string safer without changing log structure significantly.
Concretely:
- Edit
src/SourceFlow/Cloud/Security/SensitiveDataMasker.cs. - In
MaskJsonElement, replace the branch:
else if (property.Value.ValueKind == JsonValueKind.Array)
{
sb.Append(property.Value.GetRawText());
}with logic that:
- Iterates over
property.Value.EnumerateArray(). - For each element:
- If it is an object, call
MaskJsonElementrecursively with an appropriate element type (when available). - For non-object elements, just append
GetRawText()as before.
- If it is an object, call
- Produces a properly formatted JSON array string.
We must be careful with the element type when recursing; since we only know propInfo.PropertyType, which might be IEnumerable<T> or List<T>, we can extract the generic argument if present and pass it to MaskJsonElement. For non-generic collections, we can conservatively pass typeof(object) so that masking falls back to default behavior. No new external dependencies are needed; we will use standard reflection and JSON APIs already in use.
No modifications are needed in AwsSqsCommandListenerEnhanced.cs; the logging call can stay as-is, since it will now benefit from the enhanced masking.
| @@ -74,7 +74,44 @@ | ||
| } | ||
| else if (property.Value.ValueKind == JsonValueKind.Array) | ||
| { | ||
| sb.Append(property.Value.GetRawText()); | ||
| // Recursively process arrays so that nested objects can be masked | ||
| var arrayBuilder = new StringBuilder(); | ||
| arrayBuilder.Append('['); | ||
|
|
||
| bool firstElement = true; | ||
| foreach (var elementItem in property.Value.EnumerateArray()) | ||
| { | ||
| if (!firstElement) arrayBuilder.Append(','); | ||
| firstElement = false; | ||
|
|
||
| if (elementItem.ValueKind == JsonValueKind.Object && propInfo != null) | ||
| { | ||
| // Determine element type if this is a collection like IEnumerable<T> | ||
| var elementType = typeof(object); | ||
| var propType = propInfo.PropertyType; | ||
| if (propType.IsArray) | ||
| { | ||
| elementType = propType.GetElementType() ?? typeof(object); | ||
| } | ||
| else if (propType.IsGenericType) | ||
| { | ||
| var genericArgs = propType.GetGenericArguments(); | ||
| if (genericArgs.Length == 1) | ||
| { | ||
| elementType = genericArgs[0]; | ||
| } | ||
| } | ||
|
|
||
| arrayBuilder.Append(MaskJsonElement(elementItem, elementType)); | ||
| } | ||
| else | ||
| { | ||
| arrayBuilder.Append(elementItem.GetRawText()); | ||
| } | ||
| } | ||
|
|
||
| arrayBuilder.Append(']'); | ||
| sb.Append(arrayBuilder.ToString()); | ||
| } | ||
| else | ||
| { |
| // Log with masked sensitive data | ||
| _logger.LogInformation( | ||
| "Event published to AWS SNS: {EventType} -> {Topic}, Duration: {Duration}ms, Event: {Event}", | ||
| eventType, topicArn, sw.ElapsedMilliseconds, _dataMasker.Mask(@event)); |
Check warning
Code scanning / CodeQL
Exposure of private information Medium
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 15 hours ago
General approach: ensure that private information is not written to external logs unless it is deliberately and safely masked. The safest way, without changing existing call sites’ behavior, is to augment SensitiveDataMasker with an additional, stricter masking mode that can be used where we log entire events, so only explicitly allowed fields (or only masked fields) are included. Then adjust the AWS SNS dispatcher to use this stricter mode instead of the generic Mask method.
Best concrete fix with minimal behavior change elsewhere:
-
In
SensitiveDataMasker:- Add a new public method, for example
MaskForLogging(object? obj), which uses a stricter policy: by default, redact all properties unless they are explicitly annotated as non-sensitive, or alternatively only emit the masked values of properties annotated withSensitiveDataAttributeand omit others. - Implement this by adding a variant of
MaskJsonElementthat, based on the presence/absence (or type) of attributes on the property, decides whether to:- emit a masked value,
- emit a generic
"***REDACTED***", or - omit the property entirely.
- Keep the existing
Maskmethod unchanged to avoid impacting other consumers.
Since we only see
SensitiveDataAttributeandSensitiveDataType, we’ll implement a conservative policy inMaskForLogging: for any property that does NOT haveSensitiveDataAttribute, we replace its value with"***REDACTED***". For properties WITHSensitiveDataAttribute, we use the existingMaskValuelogic. This guarantees that no unmarked, potentially sensitive data is logged from this particular call site. - Add a new public method, for example
-
In
AwsSnsEventDispatcherEnhanced:- Change the log call on line 154 from
_dataMasker.Mask(@event)to_dataMasker.MaskForLogging(@event)(or whatever name we introduce). - This ensures that for event publishing logs—which can contain arbitrary payloads—we never log raw data for unannotated fields.
- Change the log call on line 154 from
Specific edits:
- File
src/SourceFlow/Cloud/Security/SensitiveDataMasker.cs:- Add a new public method
MaskForLogging(object? obj)nearMask(object? obj). - Add a private helper
MaskJsonElementForLogging(JsonElement element, Type objectType)similar toMaskJsonElement, but redacting all unannotated properties with"***REDACTED***"instead of their raw values.
- Add a new public method
- File
src/SourceFlow.Cloud.AWS/Messaging/Events/AwsSnsEventDispatcherEnhanced.cs:- Update the log statement to use
_dataMasker.MaskForLogging(@event).
- Update the log statement to use
No changes to existing imports are needed beyond what is already present.
| @@ -148,10 +148,10 @@ | ||
| _cloudMetrics.RecordEventPublished(eventType, topicArn, "aws"); | ||
| _cloudMetrics.RecordPublishDuration(sw.ElapsedMilliseconds, eventType, "aws"); | ||
|
|
||
| // Log with masked sensitive data | ||
| // Log with strongly masked sensitive data (all unannotated fields redacted) | ||
| _logger.LogInformation( | ||
| "Event published to AWS SNS: {EventType} -> {Topic}, Duration: {Duration}ms, Event: {Event}", | ||
| eventType, topicArn, sw.ElapsedMilliseconds, _dataMasker.Mask(@event)); | ||
| eventType, topicArn, sw.ElapsedMilliseconds, _dataMasker.MaskForLogging(@event)); | ||
| } | ||
| catch (CircuitBreakerOpenException cbex) | ||
| { |
| _logger.LogInformation( | ||
| "Event processed from SNS: {EventType} -> {Queue}, Duration: {Duration}ms, MessageId: {MessageId}, Event: {Event}", | ||
| eventTypeName, queueUrl, sw.ElapsedMilliseconds, message.MessageId, | ||
| _dataMasker.Mask(@event)); |
Check warning
Code scanning / CodeQL
Exposure of private information Medium
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 15 hours ago
General fix: ensure that anything that reaches logs via SensitiveDataMasker is either (a) not sensitive, or (b) irreversibly redacted to a non-identifiable form. For strong guarantees, masking routines for types like credit card, email, etc. should not preserve usable portions of the data that CodeQL (and security reviewers) consider “private information”. Where very detailed event data is not required, prefer logging high‑level metadata instead of full payloads.
Best targeted fix without changing existing behavior too much:
- Strengthen
SensitiveDataMaskerso that:- All
SensitiveDataTypevalues are masked in a fully redacted way (constant tokens or strong truncation). - Masking functions like
MaskCreditCardandMaskEmailreturn non-sensitive placeholders such as"****"or"***REDACTED***"instead of partially preserved values.
- All
- Optionally (and minimally) adjust the AWS SNS listener log statement to reduce exposure by logging a summary instead of the full masked payload, but we can resolve the CodeQL concern primarily by making the maskers produce no private data.
Given we can only edit shown snippets, we will:
- In
SensitiveDataMasker, changeMaskValueto ensure that sensitive types are redacted with non-identifying placeholders. - (If present in the shown file—though not in the snippet—we would also adjust
MaskCreditCard/MaskEmailimplementations directly. Since they are not shown, we’ll implement the stronger behavior inMaskValue, overriding their behavior by not calling them at all.)
Concretely:
- Replace the
MaskValuemethod body to:- Return
"****"(or"***REDACTED***") for all sensitive data types instead of callingMaskCreditCard,MaskEmail, etc. - This guarantees that the string returned from
SensitiveDataMasker.Maskcontains no recognizable private information, satisfying CodeQL while maintaining that logs still indicate that data existed and was redacted.
- Return
No change is strictly required in AwsSnsEventListenerEnhanced because once Mask returns only redacted representations, the log line no longer exposes private content.
| @@ -101,17 +101,20 @@ | ||
|
|
||
| private string MaskValue(string value, SensitiveDataType type) | ||
| { | ||
| // To avoid exposing private information in logs, we ensure that all | ||
| // sensitive data types are fully redacted and do not preserve | ||
| // any meaningful part of the original value. | ||
| return type switch | ||
| { | ||
| SensitiveDataType.CreditCard => MaskCreditCard(value), | ||
| SensitiveDataType.Email => MaskEmail(value), | ||
| SensitiveDataType.PhoneNumber => MaskPhoneNumber(value), | ||
| SensitiveDataType.SSN => MaskSSN(value), | ||
| SensitiveDataType.PersonalName => MaskPersonalName(value), | ||
| SensitiveDataType.IPAddress => MaskIPAddress(value), | ||
| SensitiveDataType.Password => "********", | ||
| SensitiveDataType.ApiKey => MaskApiKey(value), | ||
| _ => "***REDACTED***" | ||
| SensitiveDataType.CreditCard => "***REDACTED***", | ||
| SensitiveDataType.Email => "***REDACTED***", | ||
| SensitiveDataType.PhoneNumber => "***REDACTED***", | ||
| SensitiveDataType.SSN => "***REDACTED***", | ||
| SensitiveDataType.PersonalName => "***REDACTED***", | ||
| SensitiveDataType.IPAddress => "***REDACTED***", | ||
| SensitiveDataType.Password => "********", | ||
| SensitiveDataType.ApiKey => "***REDACTED***", | ||
| _ => "***REDACTED***" | ||
| }; | ||
| } | ||
|
|
… for implicit usings
…k container in CI
- Increased health check timeout from 30s to 90s for CI environments - Added 30 retries with 3s delay between attempts - Implemented xUnit collection fixture to share LocalStack instance - Enhanced external instance detection with 10s timeout and retry logic - Added 5s initial delay after container start in CI - Improved health check logging with individual service status - Auto-detects GitHub Actions environment via GITHUB_ACTIONS variable - Preserves local development behavior (30s timeout, 10 retries) Fixes timeout issues in GitHub Actions CI while maintaining fast local tests. Resolves port conflicts through shared fixture pattern.
SourceFlow.Net v2.0.0 - Changelog
Release Date: TBC
Status: In Development
Note: This release includes AWS cloud integration support. Azure cloud integration will be available in a future release.
🎉 Major Changes
Cloud Core Consolidation
The
SourceFlow.Cloud.Coreproject has been consolidated into the main SourceFlow package. This architectural change simplifies the dependency structure and reduces the number of separate packages required for cloud integration.Benefits:
✨ New Features
Integrated Cloud Functionality
The following components are now part of the core
SourceFlowpackage:Configuration
BusConfiguration- Fluent API for routing configurationIBusBootstrapConfiguration- Bootstrapper integrationICommandRoutingConfiguration- Command routing abstractionIEventRoutingConfiguration- Event routing abstractionIIdempotencyService- Duplicate message detectionInMemoryIdempotencyService- Default implementationIdempotencyConfigurationBuilder- Fluent API for idempotency configurationResilience
ICircuitBreaker- Circuit breaker pattern interfaceCircuitBreaker- Implementation with state managementCircuitBreakerOptions- Configuration optionsCircuitBreakerOpenException- Exception for open circuitsCircuitBreakerStateChangedEventArgs- State transition eventsSecurity
IMessageEncryption- Message encryption abstractionSensitiveDataAttribute- Marks properties for encryptionSensitiveDataMasker- Automatic log maskingEncryptionOptions- Encryption configurationDead Letter Processing
IDeadLetterProcessor- Failed message handlingIDeadLetterStore- Failed message persistenceDeadLetterRecord- Failed message modelInMemoryDeadLetterStore- Default implementationObservability
CloudActivitySource- OpenTelemetry activity sourceCloudMetrics- Standard cloud metricsCloudTelemetry- Centralized telemetrySerialization
PolymorphicJsonConverter- Handles inheritance hierarchiesIdempotency Configuration Builder
New fluent API for configuring idempotency services:
Builder Methods:
UseEFIdempotency(connectionString, cleanupIntervalMinutes)- Entity Framework-based (requires SourceFlow.Stores.EntityFramework package)UseInMemory()- In-memory implementationUseCustom<TImplementation>()- Custom implementation by typeUseCustom(factory)- Custom implementation with factory functionEnhanced AWS Integration
AWS cloud extension now supports explicit idempotency configuration:
📚 Documentation Updates
New Documentation
Updated Documentation
🐛 Bug Fixes
🔧 Internal Changes
Project Structure
src/SourceFlow.Cloud.Core/intosrc/SourceFlow/Cloud/Build System
📦 Package Dependencies
SourceFlow v2.0.0
SourceFlow.Cloud.AWS v2.0.0
SourceFlow >= 2.0.0SourceFlow.Cloud.Coredependency🚀 Upgrade Path
For AWS Extension Users
If you're using the AWS cloud extension, no code changes are required. The consolidation is transparent to consumers of the cloud package.
📝 Notes
🔗 Related Documentation
Version: 2.0.0
Date: TBC
Status: In Development