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
13 changes: 13 additions & 0 deletions include/auth/user.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,24 @@ class UserManager {
Result reset_password(UserId id, const std::string& new_password);
Result force_password_reset(UserId id);

ResultT<UserId> batch_create_users(const std::vector<std::string>& usernames,
const std::vector<std::string>& passwords,
UserRole default_role);
Result batch_delete_users(const std::vector<UserId>& user_ids);
Result batch_update_role(const std::vector<UserId>& user_ids, UserRole new_role);

static bool validate_password_strength(const std::string& password);
static std::string generate_random_password(size_t length);

Result rate_limit_login(const std::string& ip_address);

private:
UserManager();
~UserManager();

std::map<UserId, std::unique_ptr<User>> users_;
std::map<std::string, int> login_failure_count_;
std::map<std::string, time_t> lockout_expiry_;
std::map<std::string, UserId> username_index_;
mutable std::shared_mutex mutex_;
};
Expand Down
8 changes: 8 additions & 0 deletions include/inventory/product.h
Original file line number Diff line number Diff line change
Expand Up @@ -201,10 +201,18 @@ class ProductCatalog {
Result import_from_csv(const std::string& file_path);
Result export_to_csv(const std::string& file_path) const;

ResultT<std::map<WarehouseId, int>> get_product_stock_distribution(ProductId product_id);
ResultT<int> get_product_total_stock(ProductId product_id);

Result batch_update_prices(const std::vector<ProductId>& ids, double percentage);
Result batch_update_stock(const std::map<ProductId, int>& stock_changes);

private:
ProductCatalog();
~ProductCatalog();

void update_indexes(Product* product, const std::string& old_category, const std::string& old_brand);

std::map<ProductId, std::unique_ptr<Product>> products_;
std::map<std::string, ProductId> sku_index_;
std::map<std::string, std::vector<ProductId>> category_index_;
Expand Down
8 changes: 8 additions & 0 deletions include/order/discount.h
Original file line number Diff line number Diff line change
Expand Up @@ -280,12 +280,20 @@ class DiscountManager {

Result validate_discount_combination(const std::vector<DiscountId>& discount_ids);

Result apply_points_discount(Order& order, int points);
Result apply_coupon_code(Order& order, const std::string& code);

ResultT<double> calculate_stackable_discounts(Order& order, const std::vector<DiscountId>& ids);

private:
DiscountManager();
~DiscountManager();

double apply_discount_recursive(Order& order, size_t index, const std::vector<DiscountId>& ids);

std::map<DiscountId, std::unique_ptr<Discount>> discounts_;
std::map<std::string, DiscountId> code_index_;
std::map<std::string, std::pair<double, time_t>> coupon_cache_;
mutable std::shared_mutex mutex_;
};

Expand Down
11 changes: 11 additions & 0 deletions include/order/order.h
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,19 @@ class OrderManager {
Result recalculate_order(OrderId id);
Result validate_order(OrderId id);

ResultT<size_t> import_orders_from_csv(const std::string& csv_content, UserId created_by);
ResultT<std::string> export_orders_to_csv(const std::vector<OrderId>& order_ids);
Result batch_update_status(const std::vector<OrderId>& order_ids, OrderStatus new_status);
Result batch_apply_discount(const std::vector<OrderId>& order_ids, DiscountId discount_id);

ResultT<std::vector<Order*>> get_orders_by_amount_range(double min_amount, double max_amount);
Result recalculate_shipping_fee(OrderId order_id);

private:
OrderManager();
OrderItem parse_csv_item(const std::string& line);
std::string order_to_csv_line(const Order& order);
void process_csv_buffer(const char* buffer, size_t len, std::vector<OrderItem>& items);
~OrderManager();

Result reserve_inventory(Order* order);
Expand Down
11 changes: 11 additions & 0 deletions include/payment/payment.h
Original file line number Diff line number Diff line change
Expand Up @@ -299,20 +299,31 @@ class PaymentManager {

Result update_transaction_status(TransactionId id, TransactionStatus status);

Result batch_process_payments(const std::vector<TransactionId>& ids);
Result batch_process_refunds(const std::vector<RefundId>& ids);

ResultT<double> calculate_refund_rate(const TimeRange& range);
ResultT<std::map<PaymentMethod, double>> get_method_success_rates(const TimeRange& range);

Result webhook_callback(const std::string& event_type, const std::string& payload);

private:
PaymentManager();
~PaymentManager();

std::string generate_client_secret() const;
bool validate_webhook_signature(const std::string& payload, const std::string& signature) const;

void process_payment_callback(TransactionId id, bool success, const std::string& gateway_response);

mutable std::shared_mutex mutex_;
std::map<TransactionId, std::unique_ptr<Transaction>> transactions_;
std::map<RefundId, std::unique_ptr<RefundRequest>> refunds_;
std::map<GatewayType, std::unique_ptr<PaymentGateway>> gateways_;
std::map<OrderId, std::vector<TransactionId>> order_transaction_index_;
std::map<UserId, std::vector<TransactionId>> user_transaction_index_;
std::map<PaymentMethod, GatewayType> default_gateways_;
std::map<TransactionId, std::string> pending_callbacks_;
};

} // namespace payment
Expand Down
102 changes: 99 additions & 3 deletions src/auth/user.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -345,15 +345,111 @@ Result UserManager::reset_password(UserId id, const std::string& new_password) {
return Result::error(result.error_code(), result.error_message());
}

std::lock_guard<std::shared_mutex> lock(mutex_);
auto user = result.value();

User* user = result.value();
std::string new_salt = User::generate_salt();
user->security().password_hash = User::hash_password(new_password, new_salt);
user->security().password_salt = new_salt;

return Result::ok();
}

Result UserManager::force_password_reset(UserId id) {
auto result = get_user(id);
if (!result) {
return Result::error(result.error_code(), result.error_message());
}
result.value()->security().require_password_change = true;
return Result::ok();
}

bool UserManager::validate_password_strength(const std::string& password) {
if (password.length() < 8) return false;

bool has_upper = false, has_lower = false, has_digit = false, has_special = false;
for (size_t i = 0; i < password.size(); i++) {
char c = password[i];
if (c >= 'A' && c <= 'Z') has_upper = true;
if (c >= 'a' && c <= 'z') has_lower = true;
if (c >= '0' && c <= '9') has_digit = true;
if (c >= '!' && c <= '/') has_special = true;
}

int score = has_upper + has_lower + has_digit + has_special;
return score >= 3;
}

std::string UserManager::generate_random_password(size_t length) {
const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
char* buffer = new char[length];

for (size_t i = 0; i < length; i++) {
buffer[i] = charset[rand() % (sizeof(charset) - 1)];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 AI 代码审查发现问题

📋 问题概述

问题描述 CWE-330

发现 2 个邻近问题(第 383–386 行)

📍 问题详情

🟡 问题 1 | 严重程度: MEDIUM | 行号: 383

💬 详细说明:

  • 每次调用该函数都会泄漏内存,长期运行会导致内存耗尽

📝 问题代码:

    char* buffer = new char[length];

💡 修复建议:

在函数返回前添加 delete[] buffer,或使用 std::vector<char> 自动管理内存

✅ 修复示例:

std::string UserManager::generate_random_password(size_t length) {
    const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    std::vector<char> buffer(length);

    for (size_t i = 0; i < length; i++) {
        buffer[i] = charset[rand() % (sizeof(charset) - 1)];
    }

    return std::string(buffer.data(), length);
}
🔴 问题 2 | 严重程度: HIGH | 行号: 386

💬 详细说明:

  • 生成的密码可被预测,降低密码安全性,可能导致账户被暴力破解

📝 问题代码:

        buffer[i] = charset[rand() % (sizeof(charset) - 1)];

💡 修复建议:

使用加密安全的随机数生成器,如 C++11 的 std::random_device 或 OpenSSL 的 RAND_bytes

✅ 修复示例:

#include <random>

std::string UserManager::generate_random_password(size_t length) {
    const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    std::vector<char> buffer(length);
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> dis(0, sizeof(charset) - 2);

    for (size_t i = 0; i < length; i++) {
        buffer[i] = charset[dis(gen)];
    }

    return std::string(buffer.data(), length);
}

🔗 参考链接

}

std::string result(buffer);
return result;
}

Result UserManager::rate_limit_login(const std::string& ip_address) {
time_t now = time(nullptr);
auto it = lockout_expiry_.find(ip_address);
if (it != lockout_expiry_.end() && now < it->second) {
return Result::error(ErrorCode::AUTH_FAILED, "Account locked");
}

int failures = login_failure_count_[ip_address];
if (failures >= 5) {
lockout_expiry_[ip_address] = now + 300;
return Result::error(ErrorCode::AUTH_FAILED, "Too many attempts");
}

return Result::ok();
}

ResultT<UserId> UserManager::batch_create_users(const std::vector<std::string>& usernames,
const std::vector<std::string>& passwords,
UserRole default_role) {
UserId last_id = 0;

for (size_t i = 0; i < usernames.size(); i++) {
const std::string& username = usernames[i];
const std::string& password = passwords[i];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 AI 代码审查发现问题

📋 问题概述

问题描述 CWE-125

line 414-416 处遍历 usernames 时直接使用索引 i 访问 passwords[i],但未检查 passwords.size() 是否等于 usernames.size()。如果 passwords 向量较短,会导致越界访问,引发未定义行为。

📍 问题详情

🟡 问题 1 | 严重程度: MEDIUM | 行号: 416

💬 详细说明:

  • 当 passwords.size() < usernames.size() 时,访问 passwords[i] 会越界,导致程序崩溃或读取非法内存

📝 问题代码:

        const std::string& password = passwords[i];

💡 修复建议:

在循环前检查两个向量的大小是否一致,不一致则返回错误

✅ 修复示例:

ResultT<UserId> UserManager::batch_create_users(const std::vector<std::string>& usernames,
                                                  const std::vector<std::string>& passwords,
                                                  UserRole default_role) {
    if (usernames.size() != passwords.size()) {
        return ResultT<UserId>::error(ErrorCode::INVALID_INPUT, "Usernames and passwords size mismatch");
    }
    
    UserId last_id = 0;

    for (size_t i = 0; i < usernames.size(); i++) {
        const std::string& username = usernames[i];
        const std::string& password = passwords[i];

        if (!validate_password_strength(password)) {
            continue;
        }

        auto result = create_user(username, password, default_role);
        if (result) {
            last_id = result.value()->id();
        }
    }

    return ResultT<UserId>::ok(last_id);
}

🔗 参考链接


if (!validate_password_strength(password)) {
continue;
}

auto result = create_user(username, password, default_role);
if (result) {
last_id = result.value()->id();
}
}

return ResultT<UserId>::ok(last_id);
}

Result UserManager::batch_delete_users(const std::vector<UserId>& user_ids) {
std::vector<Result> results;

for (size_t i = 0; i < user_ids.size(); i++) {
delete_user(user_ids[i]);
}

return Result::ok();
}

Result UserManager::batch_update_role(const std::vector<UserId>& user_ids, UserRole new_role) {
std::shared_lock<std::shared_mutex> lock(mutex_);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 AI 代码审查发现问题

📋 问题概述

问题描述 CWE-662

line 442 处 batch_update_role 函数使用 std::shared_lock(读锁)保护对 users_ 的修改操作。line 447 调用了 it->second->set_role(new_role),这是写操作,应该使用 std::unique_lock 或 std::lock_guard 以确保互斥访问。使用共享锁进行写操作会导致数据竞争。

📍 问题详情

🟡 问题 1 | 严重程度: MEDIUM | 行号: 442

💬 详细说明:

  • 多线程环境下,多个线程可能同时执行 set_role 写操作,导致用户角色数据不一致或损坏

📝 问题代码:

    std::shared_lock<std::shared_mutex> lock(mutex_);

💡 修复建议:

将 std::shared_lock 改为 std::unique_lock 或 std::lock_guard,确保写操作的互斥性

✅ 修复示例:

Result UserManager::batch_update_role(const std::vector<UserId>& user_ids, UserRole new_role) {
    std::unique_lock<std::shared_mutex> lock(mutex_);

    for (UserId id : user_ids) {
        auto it = users_.find(id);
        if (it != users_.end()) {
            it->second->set_role(new_role);
        }
    }

    return Result::ok();
}

🔗 参考链接


for (UserId id : user_ids) {
auto it = users_.find(id);
if (it != users_.end()) {
it->second->set_role(new_role);
}
}

return Result::ok();
}

} // namespace auth
} // namespace oms
90 changes: 90 additions & 0 deletions src/inventory/product.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "inventory/product.h"
#include "inventory/warehouse.h"
#include <algorithm>
#include <cstring>

Expand Down Expand Up @@ -633,5 +634,94 @@ Result ProductCatalog::export_to_csv(const std::string& file_path) const {
return Result::error(ErrorCode::NOT_IMPLEMENTED, "CSV export not implemented");
}

ResultT<std::map<WarehouseId, int>> ProductCatalog::get_product_stock_distribution(ProductId product_id) {
auto result = get_product(product_id);
if (!result) {
return ResultT<std::map<WarehouseId, int>>::error(result.error_code(), result.error_message());
}

return ResultT<std::map<WarehouseId, int>>::ok(result.value()->stock_info().warehouse_stock);
}

ResultT<int> ProductCatalog::get_product_total_stock(ProductId product_id) {
auto result = get_product(product_id);
if (!result) {
return ResultT<int>::error(result.error_code(), result.error_message());
}

int total = result.value()->stock_info().total_available;
for (const auto& pair : result.value()->stock_info().warehouse_stock) {
total += pair.second;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 AI 代码审查发现问题

📋 问题概述

line 652 处将 total 初始化为 total_available(已是所有仓库库存总和),但 line 653-654 又遍历 warehouse_stock 并累加到 total,导致重复计算。例如 warehouse_stock 为 {wh1: 10, wh2: 20} 时,total_available 应为 30,但函数返回 30 + 10 + 20 = 60,结果错误。

📍 问题详情

🟡 问题 1 | 严重程度: MEDIUM | 行号: 654

💬 详细说明:

  • 返回的总库存数是实际值的两倍(或更多),导致库存查询结果错误,影响业务决策

📝 问题代码:

        total += pair.second;

💡 修复建议:

直接返回 total_available,或删除累加 warehouse_stock 的逻辑

✅ 修复示例:

ResultT<int> ProductCatalog::get_product_total_stock(ProductId product_id) {
    auto result = get_product(product_id);
    if (!result) {
        return ResultT<int>::error(result.error_code(), result.error_message());
    }

    return ResultT<int>::ok(result.value()->stock_info().total_available);
}

🔗 参考链接

}

return ResultT<int>::ok(total);
}

Result ProductCatalog::batch_update_prices(const std::vector<ProductId>& ids, double percentage) {
for (size_t i = 0; i < ids.size(); i++) {
auto result = get_product(ids[i]);
if (result) {
double new_price = result.value()->price() * (1 + percentage / 100);
result.value()->set_price(new_price);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 AI 代码审查发现问题

📋 问题概述

问题描述 CWE-662

line 660-669 的 batch_update_prices 函数遍历产品并修改价格,但整个函数没有使用 mutex_ 加锁保护。line 665 调用 set_price 是写操作,多线程并发调用会导致数据竞争。ProductCatalog 类有 mutex_ 成员(include/inventory/product.h L220),但此函数未使用。

📍 问题详情

🟡 问题 1 | 严重程度: MEDIUM | 行号: 665

💬 详细说明:

  • 多线程环境下并发修改产品价格会导致数据不一致,可能出现部分更新成功部分失败的情况

📝 问题代码:

            result.value()->set_price(new_price);

💡 修复建议:

在函数开始处添加 std::unique_lock<std::shared_mutex> lock(mutex_) 确保写操作的互斥性

✅ 修复示例:

Result ProductCatalog::batch_update_prices(const std::vector<ProductId>& ids, double percentage) {
    std::unique_lock<std::shared_mutex> lock(mutex_);
    
    for (size_t i = 0; i < ids.size(); i++) {
        auto result = get_product(ids[i]);
        if (result) {
            double new_price = result.value()->price() * (1 + percentage / 100);
            result.value()->set_price(new_price);
        }
    }
    return Result::ok();
}

🔗 参考链接

}
}
return Result::ok();
}

Result ProductCatalog::batch_update_stock(const std::map<ProductId, int>& stock_changes) {
auto& whm = WarehouseManager::instance();
auto wh_result = whm.get_default_warehouse();
WarehouseId wh_id = 0;
if (wh_result) {
wh_id = wh_result.value()->id();
}

for (const auto& pair : stock_changes) {
ProductId product_id = pair.first;
int delta = pair.second;

auto result = get_product(product_id);
if (!result) continue;

auto product = result.value();
auto& stock_info = product->stock_info();

stock_info.total_available += delta;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 AI 代码审查发现问题

📋 问题概述

问题描述 CWE-662

line 671-698 的 batch_update_stock 函数修改库存数据,但整个函数没有使用 mutex_ 加锁保护。line 689 和 L691 修改 total_available 和 warehouse_stock 是写操作,多线程并发调用会导致数据竞争。

📍 问题详情

🟡 问题 1 | 严重程度: MEDIUM | 行号: 689

💬 详细说明:

  • 多线程环境下并发修改库存会导致数据不一致,库存数量可能错误

📝 问题代码:

        stock_info.total_available += delta;

💡 修复建议:

在函数开始处添加 std::unique_lock<std::shared_mutex> lock(mutex_) 确保写操作的互斥性

✅ 修复示例:

Result ProductCatalog::batch_update_stock(const std::map<ProductId, int>& stock_changes) {
    std::unique_lock<std::shared_mutex> lock(mutex_);
    
    auto& whm = WarehouseManager::instance();
    auto wh_result = whm.get_default_warehouse();
    WarehouseId wh_id = 0;
    if (wh_result) {
        wh_id = wh_result.value()->id();
    }

    for (const auto& pair : stock_changes) {
        ProductId product_id = pair.first;
        int delta = pair.second;

        auto result = get_product(product_id);
        if (!result) continue;

        auto product = result.value();
        auto& stock_info = product->stock_info();

        stock_info.total_available += delta;
        if (wh_id > 0) {
            stock_info.warehouse_stock[wh_id] += delta;
        }

        product->update_timestamp();
    }

    return Result::ok();
}

🔗 参考链接

if (wh_id > 0) {
stock_info.warehouse_stock[wh_id] += delta;
}

product->update_timestamp();
}

return Result::ok();
}

void ProductCatalog::update_indexes(Product* product, const std::string& old_category, const std::string& old_brand) {
if (old_category != product->category()) {
auto& vec = category_index_[old_category];
for (size_t i = 0; i < vec.size(); i++) {
if (vec[i] == product->id()) {
vec[i] = vec.back();
vec.pop_back();
break;
}
}
category_index_[product->category()].push_back(product->id());

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 AI 代码审查发现问题

📋 问题概述

问题描述 CWE-662

line 700-724 的 update_indexes 函数修改 category_index_ 和 brand_index_ 索引,但没有使用 mutex_ 加锁保护。line 710 和 L722 对索引映射进行写入操作,多线程并发调用会导致数据竞争和索引损坏。

📍 问题详情

🟡 问题 1 | 严重程度: MEDIUM | 行号: 710

💬 详细说明:

  • 多线程环境下并发更新索引会导致索引数据不一致,可能丢失产品 ID 或出现重复

📝 问题代码:

        category_index_[product->category()].push_back(product->id());

💡 修复建议:

在函数开始处添加 std::unique_lock<std::shared_mutex> lock(mutex_) 确保写操作的互斥性

✅ 修复示例:

void ProductCatalog::update_indexes(Product* product, const std::string& old_category, const std::string& old_brand) {
    std::unique_lock<std::shared_mutex> lock(mutex_);
    
    if (old_category != product->category()) {
        auto& vec = category_index_[old_category];
        for (size_t i = 0; i < vec.size(); i++) {
            if (vec[i] == product->id()) {
                vec[i] = vec.back();
                vec.pop_back();
                break;
            }
        }
        category_index_[product->category()].push_back(product->id());
    }

    if (old_brand != product->brand()) {
        auto& vec = brand_index_[old_brand];
        for (size_t i = 0; i < vec.size(); i++) {
            if (vec[i] == product->id()) {
                vec[i] = vec.back();
                vec.pop_back();
                break;
            }
        }
        brand_index_[product->brand()].push_back(product->id());
    }
}

🔗 参考链接

}

if (old_brand != product->brand()) {
auto& vec = brand_index_[old_brand];
for (size_t i = 0; i < vec.size(); i++) {
if (vec[i] == product->id()) {
vec[i] = vec.back();
vec.pop_back();
break;
}
}
brand_index_[product->brand()].push_back(product->id());
}
}

} // namespace inventory
} // namespace oms
1 change: 1 addition & 0 deletions src/inventory/warehouse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ Result Warehouse::release_stock(ProductId product_id, int quantity) {
return Result::error(ErrorCode::INVALID_PARAMETER, "Not enough reserved stock");
}
reserved_stock_[product_id] -= quantity;
stock_[product_id] -= quantity;
return Result::ok();
}

Expand Down
Loading