Codex 审计发现 124 个问题,修复 59 个 P0-P2 级别问题: P0 (CRITICAL): - TOCTOU 竞态: TransientRateStore/SseConcurrencyGuard 改用 wp_cache_add 原子锁 - REST 路由: __return_true → check_bearer_present 轻量鉴权 - Schema 验证: messages/tools/tool_choice 添加完整 validate_callback - SQL 列名不匹配: upsert 查询对齐 SchemaManager 定义 - SQL 表名插值: 统一使用 %i 占位符 P1 (HIGH): - IP hash 加盐 (HMAC) + IP 解析统一 (context 共享) - Header 注入防护 (X-Request-Id/响应头 \r\n 过滤) - 数值参数范围校验 (temperature/top_p/penalties/max_tokens) - insert_key 错误处理 + revoke_key 审计日志 - Pipeline finally 保护 + RateLimiter rollback 双 store - wp_unslash 补全 + strtotime 返回值检查 - RedisRateStore $rid 冒号验证 - UpstreamStreamClient catch \Throwable P2 (MEDIUM): - AuditLogRepository DRY (提取 build_where_clause) - Token 估算 CJK 优化 (mb_strlen) - ResponseTransformMiddleware null 检查 - 嵌入响应 usage 数据修复 - SchemaManager autoload 优化 25 files changed, ~530 insertions, ~190 deletions Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
109 lines
2.9 KiB
PHP
109 lines
2.9 KiB
PHP
<?php
|
|
/**
|
|
* Audit Log Repository
|
|
*
|
|
* Database access layer for the API audit log.
|
|
*
|
|
* @package WPMind\Modules\ApiGateway\Admin
|
|
* @since 1.0.0
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace WPMind\Modules\ApiGateway\Admin;
|
|
|
|
/**
|
|
* Class AuditLogRepository
|
|
*
|
|
* Read-only queries for the wpmind_api_audit_log table.
|
|
*/
|
|
class AuditLogRepository {
|
|
|
|
/**
|
|
* Get the full table name.
|
|
*
|
|
* @return string
|
|
*/
|
|
private static function table(): string {
|
|
global $wpdb;
|
|
return $wpdb->prefix . 'wpmind_api_audit_log';
|
|
}
|
|
|
|
/**
|
|
* Build WHERE clause and parameter values from filters.
|
|
*
|
|
* @param array $filters Optional filters: key_id, event_type, date_from, date_to.
|
|
* @return array{string, array} Tuple of [ where_sql, values ].
|
|
*/
|
|
private static function build_where_clause( array $filters ): array {
|
|
$where = [];
|
|
$values = [];
|
|
|
|
if ( ! empty( $filters['key_id'] ) ) {
|
|
$where[] = 'key_id = %s';
|
|
$values[] = sanitize_text_field( $filters['key_id'] );
|
|
}
|
|
|
|
if ( ! empty( $filters['event_type'] ) ) {
|
|
$where[] = 'event_type = %s';
|
|
$values[] = sanitize_text_field( $filters['event_type'] );
|
|
}
|
|
|
|
if ( ! empty( $filters['date_from'] ) ) {
|
|
$where[] = 'created_at >= %s';
|
|
$values[] = sanitize_text_field( $filters['date_from'] ) . ' 00:00:00';
|
|
}
|
|
|
|
if ( ! empty( $filters['date_to'] ) ) {
|
|
$where[] = 'created_at <= %s';
|
|
$values[] = sanitize_text_field( $filters['date_to'] ) . ' 23:59:59';
|
|
}
|
|
|
|
$where_sql = ! empty( $where ) ? 'WHERE ' . implode( ' AND ', $where ) : '';
|
|
|
|
return [ $where_sql, $values ];
|
|
}
|
|
|
|
/**
|
|
* List audit log entries with pagination and filters.
|
|
*
|
|
* @param array $filters Optional filters: key_id, event_type, date_from, date_to.
|
|
* @param int $page Page number (1-based).
|
|
* @param int $per_page Items per page.
|
|
* @return array Array of row arrays.
|
|
*/
|
|
public static function list_logs( array $filters = [], int $page = 1, int $per_page = 20 ): array {
|
|
global $wpdb;
|
|
|
|
[ $where_sql, $filter_values ] = self::build_where_clause( $filters );
|
|
|
|
$offset = max( 0, ( $page - 1 ) * $per_page );
|
|
|
|
$sql = "SELECT * FROM %i {$where_sql} ORDER BY created_at DESC LIMIT %d OFFSET %d";
|
|
$params = array_merge( [ self::table() ], $filter_values, [ $per_page, $offset ] );
|
|
|
|
$results = $wpdb->get_results(
|
|
$wpdb->prepare( $sql, ...$params ),
|
|
ARRAY_A
|
|
);
|
|
|
|
return is_array( $results ) ? $results : [];
|
|
}
|
|
|
|
/**
|
|
* Count total audit log entries matching filters.
|
|
*
|
|
* @param array $filters Optional filters: key_id, event_type, date_from, date_to.
|
|
* @return int Total count.
|
|
*/
|
|
public static function count_logs( array $filters = [] ): int {
|
|
global $wpdb;
|
|
|
|
[ $where_sql, $filter_values ] = self::build_where_clause( $filters );
|
|
|
|
$sql = "SELECT COUNT(*) FROM %i {$where_sql}";
|
|
$params = array_merge( [ self::table() ], $filter_values );
|
|
|
|
return (int) $wpdb->get_var( $wpdb->prepare( $sql, ...$params ) );
|
|
}
|
|
}
|