Severity: Warning
File: src/Servy.Core/Security/SecureData.cs
Lines: 161, 174-181, 211-214
Description:
The XML doc on Decrypt(string cipherText) advertises a defensive fallback contract:
/// <returns>The decrypted plain text if successful; otherwise, the original <paramref name=\"cipherText\"/>.</returns>
The catch block backs that up with a log line that says the same thing:
catch (Exception ex) when (ex is FormatException || ex is CryptographicException)
{
Logger.Warn($\"Decryption failed for marked payload ({ex.GetType().Name}): {ex.Message} Returning original input.\");
return payload.ToString();
}
But payload is not the original cipherText. Earlier in the method:
ReadOnlySpan<char> textSpan = cipherText.AsSpan();
ReadOnlySpan<char> markerSpan = EncryptMarker.AsSpan();
bool hasMarker = textSpan.StartsWith(markerSpan, StringComparison.Ordinal);
ReadOnlySpan<char> payload = hasMarker ? textSpan.Slice(markerSpan.Length) : textSpan;
So when the input was \"SERVY_ENC:v2:Z29vZA==...\" and decryption fails (e.g. the AES key file was rotated or replaced), the fallback returns \"v2:Z29vZA==...\" — the marker prefix is stripped silently. The log message claims "Returning original input" but the actual return value is not the original input.
Operational consequences:
- Callers that round-trip values through
Decrypt then re-store them (e.g. an export-then-import flow that hits a corrupted record) will silently rewrite \"SERVY_ENC:v2:...\" → \"v2:...\", breaking subsequent decryption attempts entirely.
- Operators reading the warning log can't reconcile what they see in the DB column with what the catch block actually emitted downstream.
Suggested fix:
Return cipherText (the original parameter), not payload.ToString():
catch (Exception ex) when (ex is FormatException || ex is CryptographicException)
{
Logger.Warn($\"Decryption failed for marked payload ({ex.GetType().Name}): {ex.Message} Returning original input.\");
return cipherText;
}
If returning the post-marker payload is intentional (e.g. for a specific legacy migration path), update both the XML doc and the log message to reflect that.
Severity: Warning
File:
src/Servy.Core/Security/SecureData.csLines: 161, 174-181, 211-214
Description:
The XML doc on
Decrypt(string cipherText)advertises a defensive fallback contract:/// <returns>The decrypted plain text if successful; otherwise, the original <paramref name=\"cipherText\"/>.</returns>The catch block backs that up with a log line that says the same thing:
But
payloadis not the originalcipherText. Earlier in the method:So when the input was
\"SERVY_ENC:v2:Z29vZA==...\"and decryption fails (e.g. the AES key file was rotated or replaced), the fallback returns\"v2:Z29vZA==...\"— the marker prefix is stripped silently. The log message claims "Returning original input" but the actual return value is not the original input.Operational consequences:
Decryptthen re-store them (e.g. an export-then-import flow that hits a corrupted record) will silently rewrite\"SERVY_ENC:v2:...\"→\"v2:...\", breaking subsequent decryption attempts entirely.Suggested fix:
Return
cipherText(the original parameter), notpayload.ToString():If returning the post-marker payload is intentional (e.g. for a specific legacy migration path), update both the XML doc and the log message to reflect that.