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) {

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-362

line 356-362 force_password_reset 调用 get_user(id) (line 357) 获取用户指针,但 get_user 内部的 shared_lock 在返回时已释放。line 361 通过 result.value()->security() 获取非 const 引用并修改 require_password_change,此时无锁保护。对比 update_user_profile (line 233) 和 update_user_role (line 244),这两个方法在 get_user 后都额外加了 lock_guard。

📍 问题详情

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

💬 详细说明:

  • 多线程并发修改同一用户时,force_password_reset 与其他写操作(如 update_user_profile)存在数据竞争,导致用户状态不一致

📝 问题代码:

Result UserManager::force_password_reset(UserId id) {

💡 修复建议:

参考 update_user_role 模式,在 get_user 后添加 lock_guard 保护写操作

✅ 修复示例:

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

🔗 参考链接

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];

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-401

line 383 使用 new char[length] 分配内存,line 389 用 buffer 构造 string 后直接返回,但从未调用 delete[] buffer 释放内存。每次调用 generate_random_password 都会泄漏 length 字节的堆内存。

📍 问题详情

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

💬 详细说明:

  • 每次调用 generate_random_password 都会泄漏 length 字节的堆内存,频繁调用会导致内存耗尽

📝 问题代码:

    char* buffer = new char[length];

💡 修复建议:

在 return 前添加 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);
}

🔗 参考链接


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

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

Result UserManager::rate_limit_login(const std::string& ip_address) {

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

发现 2 个邻近问题(第 389–393 行)

📍 问题详情

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

💬 详细说明:

  • std::string 构造函数会读取 buffer 之后的内存直到遇到 '\0',导致未定义行为(读取垃圾数据或崩溃)

📝 问题代码:

    std::string result(buffer);

💡 修复建议:

使用带长度的 string 构造函数 std::string(buffer, length),避免依赖 null 终止符

✅ 修复示例:

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 | 严重程度: MEDIUM | 行号: 393

💬 详细说明:

  • 多线程并发调用 rate_limit_login 时,对 lockout_expiry_ 和 login_failure_count_ 的读写存在数据竞争,导致未定义行为(计数不准确、锁定时长错误)

📝 问题代码:

Result UserManager::rate_limit_login(const std::string& ip_address) {

💡 修复建议:

在方法开头添加锁保护,读取时使用 shared_lock,修改时使用 lock_guard

✅ 修复示例:

Result UserManager::rate_limit_login(const std::string& ip_address) {
    {
        std::shared_lock<std::shared_mutex> lock(mutex_);
        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) {
            lock.unlock();
            std::lock_guard<std::shared_mutex> write_lock(mutex_);
            lockout_expiry_[ip_address] = now + 300;
            return Result::error(ErrorCode::AUTH_FAILED, "Too many attempts");
        }
    }
    return Result::ok();
}

🔗 参考链接

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++) {

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-129

line 414 循环边界使用 usernames.size(),但 line 416 访问 passwords[i]。如果 passwords.size() < usernames.size(),将导致越界访问。两个参数独立传入,没有长度一致性校验。

📍 问题详情

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

💬 详细说明:

  • 当 passwords 向量短于 usernames 时,访问 passwords[i] 越界,导致未定义行为(读取垃圾数据或崩溃)

📝 问题代码:

    for (size_t i = 0; i < usernames.size(); 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_ARGUMENT, "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];
        // ...

🔗 参考链接

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);
}

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

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-362

line 431-438 batch_delete_users 在循环中调用 delete_user(user_ids[i]),但 delete_user 内部使用 lock_guard 修改 users_ 和 username_index_。batch_delete_users 本身无锁保护,在循环间隙其他线程可能插入操作,导致部分删除、状态不一致。此外 line 432 声明了 results 向量但未使用。

📍 问题详情

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

💬 详细说明:

  • 批量删除过程中其他线程可能插入新操作,导致部分用户被删除而部分保留,状态不一致

📝 问题代码:

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

💡 修复建议:

在方法开头添加 lock_guard 保护整个批量删除操作,确保原子性

✅ 修复示例:

Result UserManager::batch_delete_users(const std::vector<UserId>& user_ids) {
    std::lock_guard<std::shared_mutex> lock(mutex_);

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

    return Result::ok();
}

🔗 参考链接

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_);

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 代码审查发现问题

📋 问题概述

问题描述 CWE-457

L652 将 total 初始化为 total_available(已是所有仓库库存之和),L653-655 又遍历 warehouse_stock 累加一次,导致 total 翻倍。batch_update_stock 中 total_available 与 warehouse_stock 同步更新,确认 total_available 即为 warehouse_stock 的总和。

📍 问题详情

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

💬 详细说明:

  • 调用 get_product_total_stock 时返回值为实际库存的两倍,影响库存查询、报表等所有依赖该函数的场景

📝 问题代码:

total += pair.second;

💡 修复建议:

移除遍历累加逻辑,直接返回 total_available。或移除 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);
}
}
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;
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());
}

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;

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-459

L178 在 release_stock 中扣减 stock_,但 confirm_deduction (L182-190) 调用 release_stock 后,L187 又执行 stock_[product_id] -= quantity,导致同一 quantity 被扣减两次。release_stock 的语义应为释放预留(仅减 reserved_stock_),扣减实际库存是 confirm_deduction 的职责。

📍 问题详情

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

💬 详细说明:

  • 调用 confirm_deduction 时库存被扣减两次,导致库存数据不准确,可能影响后续库存查询和订单处理

📝 问题代码:

stock_[product_id] -= quantity;

💡 修复建议:

从 release_stock 中移除对 stock_ 的扣减(L178),只保留对 reserved_stock_ 的操作。confirm_deduction 中已有独立的 stock_ 扣减逻辑。

✅ 修复示例:

Result Warehouse::release_stock(ProductId product_id, int quantity) {
    if (reserved_stock_[product_id] < quantity) {
        return Result::error(ErrorCode::INVALID_PARAMETER, "Not enough reserved stock");
    }
    reserved_stock_[product_id] -= quantity;
    return Result::ok();
}

🔗 参考链接

return Result::ok();
}

Expand Down
Loading