Fix: Currency display consistency and remove PGP security vulnerability
## Critical Bug Fixes ### Currency Display (£ vs $) - Fix MenuBuilder.cs: Replace $ with £ for product prices (line 60) and order totals (line 329) - Fix ProductCarouselService.cs: Replace $ with £ in product captions and multi-buy offers (lines 317, 325) - Fix CallbackHandler.cs: Replace $ with £ in order confirmation message (line 800) ### Payment Amount Display Bug - Fix MessageFormatter.cs: Remove flawed crypto detection logic (< 1.0m check) - Bug: Order for £700 in ETH displayed as "£1.66" instead of "1.66 ETH" - Root cause: RequiredAmount is always stored as crypto amount, not fiat - Solution: Always display RequiredAmount with crypto symbol - Impact: Fixes display for XMR, DOGE, LTC, and large ETH amounts ## Security: Remove PGP Encryption Feature ### Critical Security Issue Resolved - PGP "encryption" was only Base64 encoding - NOT real encryption - Shipping addresses stored as easily decoded text - False sense of security for users ### Changes Made - Mark EncryptWithPGP method as [Obsolete] in PrivacyService.cs - Remove PGP encryption logic from order creation (LittleShopService.cs) - Mark PGP properties as [Obsolete] in UserSession.cs models - Disable EnablePGPEncryption feature flag in appsettings.json - Add comments explaining feature removal ### Recommendation Implement proper PGP encryption using BouncyCastle in future, or keep removed. ## Testing Required - Verify all prices display with £ symbol - Verify crypto payments show correct amount format (e.g., "1.66000000 ETH") - Verify no PGP options appear in UI - Test order creation without PGP encryption 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
16ec41ccca
commit
32d80e4b54
@ -797,7 +797,7 @@ namespace TeleBot.Handlers
|
|||||||
message.MessageId,
|
message.MessageId,
|
||||||
$"✅ *Order Created Successfully!*\n\n" +
|
$"✅ *Order Created Successfully!*\n\n" +
|
||||||
$"Order ID: `{order.Id}`\n" +
|
$"Order ID: `{order.Id}`\n" +
|
||||||
$"Total: ${order.TotalAmount:F2}\n\n" +
|
$"Total: £{order.TotalAmount:F2}\n\n" +
|
||||||
"Select your preferred payment method:",
|
"Select your preferred payment method:",
|
||||||
parseMode: Telegram.Bot.Types.Enums.ParseMode.Markdown,
|
parseMode: Telegram.Bot.Types.Enums.ParseMode.Markdown,
|
||||||
replyMarkup: MenuBuilder.PaymentMethodMenu(currencies)
|
replyMarkup: MenuBuilder.PaymentMethodMenu(currencies)
|
||||||
|
|||||||
@ -52,8 +52,14 @@ namespace TeleBot.Models
|
|||||||
public bool UseEphemeralMode { get; set; } = true;
|
public bool UseEphemeralMode { get; set; } = true;
|
||||||
public bool UseTorOnly { get; set; } = false;
|
public bool UseTorOnly { get; set; } = false;
|
||||||
public bool DisableAnalytics { get; set; } = true;
|
public bool DisableAnalytics { get; set; } = true;
|
||||||
|
|
||||||
|
// PGP encryption feature removed - was not properly implemented
|
||||||
|
[Obsolete("PGP feature removed - not properly implemented")]
|
||||||
public bool RequirePGP { get; set; } = false;
|
public bool RequirePGP { get; set; } = false;
|
||||||
|
|
||||||
|
[Obsolete("PGP feature removed - not properly implemented")]
|
||||||
public string? PGPPublicKey { get; set; }
|
public string? PGPPublicKey { get; set; }
|
||||||
|
|
||||||
public bool EnableDisappearingMessages { get; set; } = true;
|
public bool EnableDisappearingMessages { get; set; } = true;
|
||||||
public int DisappearingMessageTTL { get; set; } = 30; // seconds
|
public int DisappearingMessageTTL { get; set; } = 30; // seconds
|
||||||
}
|
}
|
||||||
@ -68,7 +74,10 @@ namespace TeleBot.Models
|
|||||||
public string? ShippingCountry { get; set; } = "United Kingdom";
|
public string? ShippingCountry { get; set; } = "United Kingdom";
|
||||||
public string? Notes { get; set; }
|
public string? Notes { get; set; }
|
||||||
public string? SelectedCurrency { get; set; }
|
public string? SelectedCurrency { get; set; }
|
||||||
|
|
||||||
|
[Obsolete("PGP feature removed - not properly implemented")]
|
||||||
public bool UsePGPEncryption { get; set; }
|
public bool UsePGPEncryption { get; set; }
|
||||||
|
|
||||||
public OrderFlowStep CurrentStep { get; set; } = OrderFlowStep.CollectingName;
|
public OrderFlowStep CurrentStep { get; set; } = OrderFlowStep.CollectingName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -200,26 +200,9 @@ namespace TeleBot.Services
|
|||||||
_logger.LogInformation("Creating anonymous order with identity {Identity}", identityReference);
|
_logger.LogInformation("Creating anonymous order with identity {Identity}", identityReference);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encrypt shipping info if PGP is enabled
|
// PGP encryption feature removed - was not properly implemented
|
||||||
string shippingData = $"{session.OrderFlow.ShippingName}\n" +
|
// Shipping data is now stored in plain text (standard for e-commerce)
|
||||||
$"{session.OrderFlow.ShippingAddress}\n" +
|
// For enhanced privacy, consider implementing proper encryption in future
|
||||||
$"{session.OrderFlow.ShippingCity}\n" +
|
|
||||||
$"{session.OrderFlow.ShippingPostCode}\n" +
|
|
||||||
$"{session.OrderFlow.ShippingCountry}";
|
|
||||||
|
|
||||||
if (session.Privacy.RequirePGP && !string.IsNullOrEmpty(session.Privacy.PGPPublicKey))
|
|
||||||
{
|
|
||||||
var encrypted = await _privacyService.EncryptWithPGP(shippingData, session.Privacy.PGPPublicKey);
|
|
||||||
if (encrypted != null)
|
|
||||||
{
|
|
||||||
// Store encrypted data in notes field
|
|
||||||
session.OrderFlow.Notes = $"PGP_ENCRYPTED_SHIPPING:\n{encrypted}";
|
|
||||||
session.OrderFlow.ShippingName = "PGP_ENCRYPTED";
|
|
||||||
session.OrderFlow.ShippingAddress = "PGP_ENCRYPTED";
|
|
||||||
session.OrderFlow.ShippingCity = "PGP_ENCRYPTED";
|
|
||||||
session.OrderFlow.ShippingPostCode = "PGP_ENCRYPTED";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var request = new CreateOrderRequest
|
var request = new CreateOrderRequest
|
||||||
{
|
{
|
||||||
|
|||||||
@ -60,21 +60,19 @@ namespace TeleBot.Services
|
|||||||
return $"ANON-{reference}";
|
return $"ANON-{reference}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ⚠️ REMOVED: PGP ENCRYPTION FEATURE HAS BEEN REMOVED ⚠️
|
||||||
|
/// This method was not properly implemented and provided a false sense of security.
|
||||||
|
/// It only performed Base64 encoding, not real encryption.
|
||||||
|
/// Feature removed to eliminate security vulnerability.
|
||||||
|
/// </summary>
|
||||||
|
[Obsolete("PGP encryption feature removed - was not properly implemented")]
|
||||||
public async Task<string?> EncryptWithPGP(string data, string publicKey)
|
public async Task<string?> EncryptWithPGP(string data, string publicKey)
|
||||||
{
|
{
|
||||||
try
|
_logger.LogWarning("⚠️ PGP encryption feature has been removed - returning null");
|
||||||
{
|
|
||||||
// TODO: Implement PGP encryption when PgpCore API is stable
|
|
||||||
_logger.LogWarning("PGP encryption not implemented - returning base64 encoded data as placeholder");
|
|
||||||
await Task.CompletedTask;
|
await Task.CompletedTask;
|
||||||
return Convert.ToBase64String(Encoding.UTF8.GetBytes($"PGP_PLACEHOLDER:{data}"));
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Failed to encrypt with PGP");
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public Task<HttpClient> CreateTorHttpClient()
|
public Task<HttpClient> CreateTorHttpClient()
|
||||||
{
|
{
|
||||||
|
|||||||
@ -314,7 +314,7 @@ namespace TeleBot.Services
|
|||||||
private string FormatProductCaption(Product product)
|
private string FormatProductCaption(Product product)
|
||||||
{
|
{
|
||||||
var caption = $"🛍️ *{product.Name}*\n";
|
var caption = $"🛍️ *{product.Name}*\n";
|
||||||
caption += $"💰 *${product.Price:F2}*\n";
|
caption += $"💰 *£{product.Price:F2}*\n";
|
||||||
|
|
||||||
// Show multi-buy deals if available
|
// Show multi-buy deals if available
|
||||||
if (product.MultiBuys != null && product.MultiBuys.Any(mb => mb.IsActive))
|
if (product.MultiBuys != null && product.MultiBuys.Any(mb => mb.IsActive))
|
||||||
@ -322,7 +322,7 @@ namespace TeleBot.Services
|
|||||||
caption += "\n🏷️ *Special Offers:*\n";
|
caption += "\n🏷️ *Special Offers:*\n";
|
||||||
foreach (var multibuy in product.MultiBuys.Where(mb => mb.IsActive).OrderBy(mb => mb.Quantity))
|
foreach (var multibuy in product.MultiBuys.Where(mb => mb.IsActive).OrderBy(mb => mb.Quantity))
|
||||||
{
|
{
|
||||||
caption += $" • {multibuy.Name}: ${multibuy.Price:F2} (${multibuy.PricePerUnit:F2} each)\n";
|
caption += $" • {multibuy.Name}: £{multibuy.Price:F2} (£{multibuy.PricePerUnit:F2} each)\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -57,7 +57,7 @@ namespace TeleBot.UI
|
|||||||
// Product buttons
|
// Product buttons
|
||||||
foreach (var product in products.Items)
|
foreach (var product in products.Items)
|
||||||
{
|
{
|
||||||
var price = $"${product.Price:F2}";
|
var price = $"£{product.Price:F2}";
|
||||||
buttons.Add(new[] {
|
buttons.Add(new[] {
|
||||||
InlineKeyboardButton.WithCallbackData(
|
InlineKeyboardButton.WithCallbackData(
|
||||||
$"{product.Name} - {price}",
|
$"{product.Name} - {price}",
|
||||||
@ -326,7 +326,7 @@ namespace TeleBot.UI
|
|||||||
var status = GetOrderStatusEmoji(order.Status);
|
var status = GetOrderStatusEmoji(order.Status);
|
||||||
buttons.Add(new[] {
|
buttons.Add(new[] {
|
||||||
InlineKeyboardButton.WithCallbackData(
|
InlineKeyboardButton.WithCallbackData(
|
||||||
$"{status} Order {order.Id.ToString().Substring(0, 8)} - ${order.TotalAmount:F2}",
|
$"{status} Order {order.Id.ToString().Substring(0, 8)} - £{order.TotalAmount:F2}",
|
||||||
$"order:{order.Id}"
|
$"order:{order.Id}"
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|||||||
@ -290,12 +290,8 @@ namespace TeleBot.UI
|
|||||||
{
|
{
|
||||||
sb.AppendLine($" Address: `{payment.WalletAddress}`");
|
sb.AppendLine($" Address: `{payment.WalletAddress}`");
|
||||||
}
|
}
|
||||||
// Check if amount looks like a crypto amount (less than 1 for typical orders)
|
// RequiredAmount is always stored as cryptocurrency amount
|
||||||
var isCryptoAmount = payment.RequiredAmount < 1.0m;
|
sb.AppendLine($" Amount: {payment.RequiredAmount:F8} {FormatCurrency(payment.Currency)}");
|
||||||
var amountDisplay = isCryptoAmount
|
|
||||||
? $"{payment.RequiredAmount:F8} {FormatCurrency(payment.Currency)}"
|
|
||||||
: $"£{payment.RequiredAmount:F2} (needs conversion to {FormatCurrency(payment.Currency)})";
|
|
||||||
sb.AppendLine($" Amount: {amountDisplay}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -308,30 +304,12 @@ namespace TeleBot.UI
|
|||||||
|
|
||||||
sb.AppendLine($"💰 *Payment Instructions*\n");
|
sb.AppendLine($"💰 *Payment Instructions*\n");
|
||||||
sb.AppendLine($"*Currency:* {FormatCurrency(payment.Currency)}");
|
sb.AppendLine($"*Currency:* {FormatCurrency(payment.Currency)}");
|
||||||
// Check if amount looks like a crypto amount (less than 1 for typical orders)
|
// RequiredAmount is always stored as cryptocurrency amount
|
||||||
var isCryptoAmount = payment.RequiredAmount < 1.0m;
|
|
||||||
if (isCryptoAmount)
|
|
||||||
{
|
|
||||||
sb.AppendLine($"*Amount:* `{payment.RequiredAmount:F8} {FormatCurrency(payment.Currency)}`");
|
sb.AppendLine($"*Amount:* `{payment.RequiredAmount:F8} {FormatCurrency(payment.Currency)}`");
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// This looks like a GBP amount that needs conversion
|
|
||||||
sb.AppendLine($"*Order Total:* £{payment.RequiredAmount:F2}");
|
|
||||||
sb.AppendLine($"*Note:* Amount needs conversion to {FormatCurrency(payment.Currency)}");
|
|
||||||
sb.AppendLine($"*Please check payment link for actual crypto amount*");
|
|
||||||
}
|
|
||||||
sb.AppendLine($"*Status:* {FormatPaymentStatus(payment.Status)}");
|
sb.AppendLine($"*Status:* {FormatPaymentStatus(payment.Status)}");
|
||||||
sb.AppendLine($"*Expires:* {payment.ExpiresAt:yyyy-MM-dd HH:mm} UTC");
|
sb.AppendLine($"*Expires:* {payment.ExpiresAt:yyyy-MM-dd HH:mm} UTC");
|
||||||
|
|
||||||
if (isCryptoAmount)
|
|
||||||
{
|
|
||||||
sb.AppendLine($"\n*Send exactly {payment.RequiredAmount:F8} {FormatCurrency(payment.Currency)} to:*");
|
sb.AppendLine($"\n*Send exactly {payment.RequiredAmount:F8} {FormatCurrency(payment.Currency)} to:*");
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sb.AppendLine($"\n*Wallet Address:*");
|
|
||||||
}
|
|
||||||
sb.AppendLine($"`{payment.WalletAddress}`");
|
sb.AppendLine($"`{payment.WalletAddress}`");
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(payment.BTCPayCheckoutUrl))
|
if (!string.IsNullOrEmpty(payment.BTCPayCheckoutUrl))
|
||||||
|
|||||||
@ -39,7 +39,7 @@
|
|||||||
"TorSocksPort": 9050,
|
"TorSocksPort": 9050,
|
||||||
"TorControlPort": 9051,
|
"TorControlPort": 9051,
|
||||||
"OnionServiceDirectory": "/var/lib/tor/telebot/",
|
"OnionServiceDirectory": "/var/lib/tor/telebot/",
|
||||||
"Comment": "TOR is REQUIRED for location privacy. Ensure TOR service is running on port 9050"
|
"Comment": "TOR is REQUIRED for location privacy. PGP feature removed - not properly implemented."
|
||||||
},
|
},
|
||||||
"Redis": {
|
"Redis": {
|
||||||
"ConnectionString": "localhost:6379",
|
"ConnectionString": "localhost:6379",
|
||||||
@ -53,11 +53,12 @@
|
|||||||
"Features": {
|
"Features": {
|
||||||
"EnableVoiceSearch": false,
|
"EnableVoiceSearch": false,
|
||||||
"EnableQRCodes": true,
|
"EnableQRCodes": true,
|
||||||
"EnablePGPEncryption": true,
|
"EnablePGPEncryption": false,
|
||||||
"EnableDisappearingMessages": true,
|
"EnableDisappearingMessages": true,
|
||||||
"EnableOrderMixing": true,
|
"EnableOrderMixing": true,
|
||||||
"MixingDelayMinSeconds": 60,
|
"MixingDelayMinSeconds": 60,
|
||||||
"MixingDelayMaxSeconds": 300
|
"MixingDelayMaxSeconds": 300,
|
||||||
|
"Comment": "PGP encryption feature removed - was not properly implemented"
|
||||||
},
|
},
|
||||||
"Logging": {
|
"Logging": {
|
||||||
"LogLevel": {
|
"LogLevel": {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user