日志升级与缓存修复最终版

This commit is contained in:
root
2026-06-16 04:58:56 +08:00
parent bcb9f293a6
commit 6b3b99b0f1
61 changed files with 2405 additions and 333 deletions
+30
View File
@@ -0,0 +1,30 @@
<?php
/** 回归:白名单 IP 快速路径 */
define('SHOW_SITE_MODE_SWITCH', 'ip_check');
define('SHOW_SITE_IP', '8.8.8.8');
define('BLACKLIST_GROUP_ID', 0);
define('WHITELIST_GROUP_ID', 0);
define('SHOW_SITE_COUNTRY', 'US');
define('CLOAK_SHOW_CONTENT', '302');
define('DB_FP', ['https://example.com/fp']);
define('COSTM_IP_SCORE', 'test');
define('BLACK_LIST', '0');
define('AUTO_BLACK', 'OFF');
define('IS_VIRTUAL', '0');
define('AUTO_BLACK_TIMES', '5');
define('WHITE_PARAMS', '');
define('IPHONE_MODEL', 0);
define('SHOW_SITE_MOBILE', 0);
define('SHOW_SITE_OUTPUT', 1);
define('WRITE_LOG', 0);
define('CLOAK_ZH_ON', 'OFF');
define('CLOAK_OS_ON', 'OFF');
define('KEEP_PARAMS', 'OFF');
define('CLOAK_REDIRECT_METHOD', '302');
define('DB_ZP', 'https://example.com/zp');
define('CLOAK_MOBILE_ON', 'OFF');
define('CLOAK_V_REFERER', 'OFF');
define('CLOAK_RISK_NUMBER', '0');
define('CLOAK_RISK_ENHANCED', 'OFF');
define('CLOAK_URL_ARGS_TIMEOUT', '0');
define('CLOAK_DEBUG_MODE', 'OFF');
+30
View File
@@ -0,0 +1,30 @@
<?php
/** 回归:正品模式下白名单 IP 仍应走真实页判定 */
define('SHOW_SITE_MODE_SWITCH', 'zp');
define('SHOW_SITE_IP', '8.8.8.8');
define('BLACKLIST_GROUP_ID', 0);
define('WHITELIST_GROUP_ID', 0);
define('SHOW_SITE_COUNTRY', 'US');
define('CLOAK_SHOW_CONTENT', '302');
define('DB_FP', ['https://example.com/fp']);
define('COSTM_IP_SCORE', 'test');
define('BLACK_LIST', '0');
define('AUTO_BLACK', 'OFF');
define('IS_VIRTUAL', '0');
define('AUTO_BLACK_TIMES', '5');
define('WHITE_PARAMS', '');
define('IPHONE_MODEL', 0);
define('SHOW_SITE_MOBILE', 0);
define('SHOW_SITE_OUTPUT', 1);
define('WRITE_LOG', 0);
define('CLOAK_ZH_ON', 'OFF');
define('CLOAK_OS_ON', 'OFF');
define('KEEP_PARAMS', 'OFF');
define('CLOAK_REDIRECT_METHOD', '302');
define('DB_ZP', 'https://example.com/zp');
define('CLOAK_MOBILE_ON', 'OFF');
define('CLOAK_V_REFERER', 'OFF');
define('CLOAK_RISK_NUMBER', '0');
define('CLOAK_RISK_ENHANCED', 'OFF');
define('CLOAK_URL_ARGS_TIMEOUT', '0');
define('CLOAK_DEBUG_MODE', 'OFF');
+30
View File
@@ -137,6 +137,11 @@ reg_assert('fp 模式配置生效', ($fpResult['mode'] ?? '') === 'fp', json_enc
$zpResult = run_pipeline_subprocess($root, 'regression_zp.php');
reg_assert('zp 模式配置生效', ($zpResult['mode'] ?? '') === 'zp', json_encode($zpResult, JSON_UNESCAPED_UNICODE));
reg_assert('zp 模式判定 false + 正品模式', ($zpResult['result'] ?? null) === 'false' && ($zpResult['reason'] ?? '') === '正品模式', json_encode($zpResult, JSON_UNESCAPED_UNICODE));
$earlyRouteOut = shell_exec(escapeshellarg(PHP_BINARY) . ' ' . escapeshellarg($root . '/tools/verify_early_route_gate.php') . ' 2>/dev/null');
$earlyRouteJson = json_decode((string) $earlyRouteOut, true);
reg_assert('白名单/正品早期快速路径', ($earlyRouteJson['ok'] ?? false) === true, (string) $earlyRouteOut);
// ── 7. 指纹跳转模板 ───────────────────────────────────────────
echo "[7] 指纹跳转模板\n";
@@ -158,12 +163,17 @@ reg_assert('cloak_fingerprint_cookies_ready', cloak_fingerprint_cookies_ready())
$_SERVER['HTTP_HOST'] = 'shop.example.com';
reg_assert('cloak_fingerprint_cookie_domain', cloak_fingerprint_cookie_domain() === '.example.com');
$cookieDomainOut = shell_exec(escapeshellarg(PHP_BINARY) . ' ' . escapeshellarg($root . '/tools/verify_cookie_domain.php') . ' 2>/dev/null');
$cookieDomainJson = json_decode((string) $cookieDomainOut, true);
reg_assert('Cookie 域推断与 JS 选项', ($cookieDomainJson['ok'] ?? false) === true, (string) $cookieDomainOut);
$_GET = ['c' => 'index'];
ob_start();
include $root . '/dist/index.php';
$distJs = ob_get_clean();
reg_assert('dist/index.php 输出 Cookies.set', strpos($distJs, "fpCookies.set('ctime'") !== false);
reg_assert('dist/index.php 输出指纹完成回调', strpos($distJs, '__cloakFingerprintDone') !== false);
reg_assert('dist/index.php 输出 sameSite Lax', strpos($distJs, '"sameSite":"Lax"') !== false || strpos($distJs, '"sameSite": "Lax"') !== false);
reg_assert('dist/index.php 无 PHP Fatal', stripos($distJs, 'Fatal error') === false);
$virtualDetectOut = shell_exec(escapeshellarg(PHP_BINARY) . ' ' . escapeshellarg($root . '/tools/verify_virtual_detect.php') . ' 2>/dev/null');
@@ -234,10 +244,30 @@ $riskCacheOut = shell_exec(escapeshellarg(PHP_BINARY) . ' ' . escapeshellarg($ro
$riskCacheJson = json_decode((string) $riskCacheOut, true);
reg_assert('二次风控 Session 缓存与 CORS', ($riskCacheJson['ok'] ?? false) === true, (string) $riskCacheOut);
$judgmentCacheOut = shell_exec(escapeshellarg(PHP_BINARY) . ' ' . escapeshellarg($root . '/tools/verify_client_judgment_cache.php') . ' 2>/dev/null');
$judgmentCacheJson = json_decode((string) $judgmentCacheOut, true);
reg_assert('客户端 Session 统一缓存策略', ($judgmentCacheJson['ok'] ?? false) === true, (string) $judgmentCacheOut);
$datalistHelperOut = shell_exec(escapeshellarg(PHP_BINARY) . ' ' . escapeshellarg($root . '/tools/verify_datalist_helper.php') . ' 2>/dev/null');
$datalistHelperJson = json_decode((string) $datalistHelperOut, true);
reg_assert('日志列表 VisitorLogListHelper', ($datalistHelperJson['ok'] ?? false) === true, (string) $datalistHelperOut);
$riskSessionOut = shell_exec(escapeshellarg(PHP_BINARY) . ' ' . escapeshellarg($root . '/tools/verify_risk_session.php') . ' 2>/dev/null');
$riskSessionJson = json_decode((string) $riskSessionOut, true);
reg_assert('二次风控 Session 防重复写库', ($riskSessionJson['ok'] ?? false) === true, (string) $riskSessionOut);
$judgmentPriOut = shell_exec(escapeshellarg(PHP_BINARY) . ' ' . escapeshellarg($root . '/tools/verify_judgment_priority.php') . ' 2>/dev/null');
$judgmentPriJson = json_decode((string) $judgmentPriOut, true);
reg_assert('判定优先级顺序', ($judgmentPriJson['ok'] ?? false) === true, (string) $judgmentPriOut);
$visitLogOut = shell_exec(escapeshellarg(PHP_BINARY) . ' ' . escapeshellarg($root . '/tools/verify_visit_log_lifecycle.php') . ' 2>/dev/null');
$visitLogJson = json_decode((string) $visitLogOut, true);
reg_assert('Visit 生命周期与 reason 规范化', ($visitLogJson['ok'] ?? false) === true, (string) $visitLogOut);
$waitLogOut = shell_exec(escapeshellarg(PHP_BINARY) . ' ' . escapeshellarg($root . '/tools/verify_wait_log_write.php') . ' 2>/dev/null');
$waitLogJson = json_decode((string) $waitLogOut, true);
reg_assert('二次风控 wait 日志同步完整落库', ($waitLogJson['ok'] ?? false) === true, (string) $waitLogOut);
// ── 12. ConfigLoader / 宝塔部署文件 ───────────────────────────
echo "[12] ConfigLoader 与宝塔部署\n";
require_once $root . '/config/ConfigLoader.php';
+97
View File
@@ -0,0 +1,97 @@
#!/usr/bin/env php
<?php
/**
* 客户端 Session 统一缓存策略回归
*/
if (PHP_SAPI !== 'cli') {
exit(1);
}
$root = dirname(__DIR__);
require_once $root . '/cong.php';
require_once $root . '/lib/Cloak/RiskSecondaryGate.php';
require_once $root . '/lib/Cloak/CloakJudgmentCache.php';
if (!defined('CLOAK_RISK_NUMBER')) {
define('CLOAK_RISK_NUMBER', 51);
}
if (!defined('COSTM_IP_SCORE')) {
define('COSTM_IP_SCORE', 'judgment_cache_test');
}
$_SESSION = [];
$tests = [];
// byApi + 风险开启 → partial
CloakJudgmentCache::storeFromByApi([
'ip' => '10.0.0.1',
'country' => 'US',
'result' => 'true',
'reason' => 'api pass',
]);
$tests['byapi_with_risk_is_partial'] = CloakJudgmentCache::isPartial();
$tests['partial_not_final'] = !CloakJudgmentCache::isFinal();
$tests['partial_fast_path_miss'] = CloakJudgmentCache::getFinal() === null;
$cachedPartial = CloakJudgmentCache::get();
if (CloakJudgmentCache::isPartial($cachedPartial)) {
CloakJudgmentCache::applyToSession($cachedPartial);
$_SESSION['visit_to_2'] = '2';
}
$tests['partial_resume_hit'] = CloakJudgmentCache::isPartial($cachedPartial);
$tests['partial_resume_sets_visit_to_2'] = ($_SESSION['visit_to_2'] ?? '') === '2';
$tests['partial_keeps_visit_to_3_open'] = ($_SESSION['visit_to_3'] ?? '') === '1';
// byApi false → final even with risk
$_SESSION = [];
CloakJudgmentCache::storeFromByApi([
'ip' => '10.0.0.2',
'country' => 'US',
'result' => 'false',
'reason' => 'api deny',
]);
$tests['byapi_false_is_final'] = CloakJudgmentCache::isFinal();
// byApiRisk → final
$_SESSION = [];
CloakJudgmentCache::storeFromByApiRisk([
'ip' => '10.0.0.3',
'country' => 'CA',
'result' => 'true',
'reason' => '',
'fp_url' => 'https://fp/x',
]);
$finalCached = CloakJudgmentCache::getFinal();
$tests['final_fast_path_hit'] = $finalCached !== null;
if (is_array($finalCached)) {
CloakJudgmentCache::applyToSession($finalCached);
}
$tests['final_applies_check_result'] = ($_SESSION['check_result'] ?? '') === 'true';
// 无 Session 缓存
$_SESSION = [];
$tests['empty_session_no_cache'] = CloakJudgmentCache::get() === null;
$noRiskCmd = escapeshellarg(PHP_BINARY) . ' -r ' . escapeshellarg(
'define("CLOAK_RISK_NUMBER",0);'
. 'require ' . var_export($root . '/lib/Cloak/RiskSecondaryGate.php', true) . ';'
. 'require ' . var_export($root . '/lib/Cloak/CloakJudgmentCache.php', true) . ';'
. '$_SESSION=[];'
. 'CloakJudgmentCache::storeFromByApi(["ip"=>"10.0.0.4","country"=>"US","result"=>"true","reason"=>""]);'
. '$c=$_SESSION["cloak_judgment_cache"]??[];'
. 'echo (CloakJudgmentCache::isFinal() && ($c["stage"]??"")==="final" && CloakJudgmentCache::getFinal()!==null) ? "1" : "0";'
);
$tests['byapi_no_risk_is_final'] = trim((string) shell_exec($noRiskCmd)) === '1';
$legacyByapiCmd = escapeshellarg(PHP_BINARY) . ' -r ' . escapeshellarg(
'define("CLOAK_RISK_NUMBER",0);'
. 'require ' . var_export($root . '/lib/Cloak/RiskSecondaryGate.php', true) . ';'
. 'require ' . var_export($root . '/lib/Cloak/CloakJudgmentCache.php', true) . ';'
. '$_SESSION=["cloak_judgment_cache"=>["stage"=>"byapi","ip"=>"10.0.0.5","country"=>"US","result"=>"true","reason"=>"","fp_url"=>"","risk_number"=>0,"campaign_id"=>"x","cached_at"=>1]];'
. 'echo (CloakJudgmentCache::isFinal() && CloakJudgmentCache::getFinal()!==null && !CloakJudgmentCache::isPartial()) ? "1" : "0";'
);
$tests['risk_zero_treats_legacy_byapi_as_final'] = trim((string) shell_exec($legacyByapiCmd)) === '1';
$ok = !in_array(false, $tests, true);
echo json_encode(['ok' => $ok, 'tests' => $tests], JSON_UNESCAPED_UNICODE) . "\n";
exit($ok ? 0 : 1);
+56
View File
@@ -0,0 +1,56 @@
#!/usr/bin/env php
<?php
/**
* Cookie 域推断与指纹 JS 选项回归(CLI JSON
*/
if (PHP_SAPI !== 'cli') {
exit(1);
}
$root = dirname(__DIR__);
require_once $root . '/lib/CloakHttpHelpers.php';
$tests = [];
$cases = [
['shop.example.com', '.example.com'],
['shop.example.com:443', '.example.com'],
['SHOP.Example.COM:8080', '.example.com'],
['shili.buzz', '.shili.buzz'],
['www.shili.buzz', '.shili.buzz'],
['a.b.shili.buzz', '.shili.buzz'],
['shop.foo.co.uk', '.foo.co.uk'],
['www.shop.foo.co.uk', '.foo.co.uk'],
['127.0.0.1', ''],
['[::1]', ''],
['localhost', ''],
['cloaka.test', ''],
['single', ''],
];
foreach ($cases as $i => $case) {
$_SERVER['HTTP_HOST'] = $case[0];
$tests['domain_' . $i] = cloak_fingerprint_cookie_domain() === $case[1];
}
$_SERVER['HTTP_HOST'] = 'shop.example.com';
$_SERVER['HTTPS'] = 'on';
$jsOpts = cloak_fingerprint_js_cookie_options();
$tests['js_opts_domain'] = ($jsOpts['domain'] ?? '') === '.example.com';
$tests['js_opts_secure_https'] = !empty($jsOpts['secure']);
$tests['js_opts_samesite'] = ($jsOpts['sameSite'] ?? '') === 'Lax';
$_SERVER['HTTPS'] = 'off';
unset($_SERVER['HTTP_X_FORWARDED_PROTO']);
$jsOptsHttp = cloak_fingerprint_js_cookie_options();
$tests['js_opts_no_secure_http'] = empty($jsOptsHttp['secure']);
if (!defined('CLOAK_COOKIE_DOMAIN')) {
define('CLOAK_COOKIE_DOMAIN', '.fixed.buzz');
}
$_SERVER['HTTP_HOST'] = 'other.example.com';
$tests['config_override'] = cloak_fingerprint_cookie_domain() === '.fixed.buzz';
$ok = !in_array(false, $tests, true);
echo json_encode(['ok' => $ok, 'tests' => $tests], JSON_UNESCAPED_UNICODE) . "\n";
exit($ok ? 0 : 1);
Regular → Executable
+9
View File
@@ -23,6 +23,15 @@ if ($conn->connect_error) {
$where = VisitorLogListHelper::buildWhere($conn, ['visit_date' => '2026-01-15']);
$tests['date_range_where'] = strpos($where, 'visit_date') !== false && strpos($where, 'DATE_FORMAT') === false;
$whereAll = VisitorLogListHelper::buildWhere($conn, []);
$tests['default_includes_wait'] = strpos($whereAll, 'wait') === false;
$whereWait = VisitorLogListHelper::buildWhere($conn, ['result' => 'wait']);
$tests['filter_wait'] = strpos($whereWait, "`result` = 'wait'") !== false;
$whereWaitCn = VisitorLogListHelper::buildWhere($conn, ['result' => '检测中']);
$tests['filter_wait_cn'] = strpos($whereWaitCn, "`result` = 'wait'") !== false;
$sql = VisitorLogListHelper::listSelectSql('1', 0, 10, true);
$tests['lean_select_no_fp_hdata'] = strpos($sql, '`fp_hdata`,') === false && strpos($sql, 'has_fp_hdata') !== false;
+82
View File
@@ -0,0 +1,82 @@
<?php
/**
* 白名单 / 正品模式早期快速路径验证
* 输出 JSON{ok, checks: [...]}
*/
$root = dirname(__DIR__);
require_once $root . '/cong.php';
require_once $root . '/lib/bootstrap.php';
$checks = [];
function check_gate($name, $cond, $detail = '')
{
global $checks;
$checks[] = ['name' => $name, 'ok' => (bool) $cond, 'detail' => $detail];
}
function run_pipeline_fixture($root, $fixtureName)
{
$fixture = $root . '/tools/fixtures/' . $fixtureName;
$cmd = escapeshellarg(PHP_BINARY) . ' ' . escapeshellarg($root . '/tools/run_pipeline_fixture.php')
. ' ' . escapeshellarg($fixture) . ' 2>&1';
exec($cmd, $out, $code);
$json = json_decode(implode("\n", $out), true);
return is_array($json) ? $json : ['error' => implode("\n", $out), 'code' => $code];
}
// WhitelistGate(独立子进程,避免常量冲突)
$gateCmd = escapeshellarg(PHP_BINARY) . ' -r ' . escapeshellarg(
'define("SHOW_SITE_IP","8.8.8.8");define("WHITE_PARAMS","token=abc");define("WHITELIST_GROUP_ID",0);'
. 'require ' . var_export($root . '/lib/bootstrap.php', true) . ';'
. '$_GET=["token"=>"abc"];'
. 'echo json_encode(["ip"=>WhitelistGate::matchesWhitelistIp("8.8.8.8"),"param"=>WhitelistGate::matchesWhitelistParams()]);'
);
$gateJson = json_decode((string) shell_exec($gateCmd), true);
check_gate('WhitelistGate IP/参数', ($gateJson['ip'] ?? false) && ($gateJson['param'] ?? false));
$zp = run_pipeline_fixture($root, 'regression_zp.php');
check_gate(
'zp 模式仅记录国家并判定 false',
($zp['mode'] ?? '') === 'zp'
&& ($zp['result'] ?? null) === 'false'
&& ($zp['reason'] ?? '') === '正品模式',
json_encode($zp, JSON_UNESCAPED_UNICODE)
);
$wl = run_pipeline_fixture($root, 'regression_whitelist_fast.php');
check_gate(
'白名单快速路径判定 true',
($wl['result'] ?? null) === 'true',
json_encode($wl, JSON_UNESCAPED_UNICODE)
);
$zpWl = run_pipeline_fixture($root, 'regression_zp_whitelist.php');
check_gate(
'zp 模式下白名单仍判定 true',
($zpWl['mode'] ?? '') === 'zp'
&& ($zpWl['result'] ?? null) === 'true',
json_encode($zpWl, JSON_UNESCAPED_UNICODE)
);
$simOut = shell_exec(escapeshellarg(PHP_BINARY) . ' ' . escapeshellarg($root . '/tools/verify_simulation_zp_whitelist_route.php') . ' 2>/dev/null');
$simJson = null;
if (preg_match('/\{.*\}\s*$/s', (string) $simOut, $m)) {
$simJson = json_decode($m[0], true);
}
check_gate(
'SimulationRouteResolver zp+白名单走真实页',
($simJson['ok'] ?? false) === true,
(string) $simOut
);
$ok = true;
foreach ($checks as $c) {
if (!$c['ok']) {
$ok = false;
break;
}
}
echo json_encode(['ok' => $ok, 'checks' => $checks], JSON_UNESCAPED_UNICODE);
+84
View File
@@ -0,0 +1,84 @@
#!/usr/bin/env php
<?php
/**
* 判定优先级顺序回归
*/
if (PHP_SAPI !== 'cli') {
exit(1);
}
$root = dirname(__DIR__);
require_once $root . '/cong.php';
require_once $root . '/lib/Cloak/WhitelistGate.php';
require_once $root . '/lib/Cloak/RiskSecondaryGate.php';
require_once $root . '/lib/Cloak/CloakJudgmentCache.php';
require_once $root . '/lib/Cloak/Pipeline/CheckPipeline.php';
if (!defined('WHITE_PARAMS')) {
define('WHITE_PARAMS', 'token=abc');
}
if (!defined('SHOW_SITE_IP')) {
define('SHOW_SITE_IP', '8.8.8.8');
}
if (!defined('BLACKLIST_GROUP_ID')) {
define('BLACKLIST_GROUP_ID', 0);
}
$tests = [];
$_GET = ['token' => 'abc'];
$tests['params_whitelist_fast'] = CheckPipeline::resolveWhitelistParamsFastPath();
$_GET = [];
$_SERVER['REMOTE_ADDR'] = '8.8.8.8';
$tests['ip_whitelist_when_no_params'] = CheckPipeline::resolveWhitelistIpFastPath();
$_GET = ['token' => 'abc'];
$tests['match_reason_params_first'] = WhitelistGate::matchReason((object) ['v_ip' => '8.8.8.8']) === '白名单链接参数';
$_GET = [];
$tests['match_reason_ip'] = WhitelistGate::matchReason((object) ['v_ip' => '8.8.8.8']) === '白名单';
$zpCmd = escapeshellarg(PHP_BINARY) . ' -r ' . escapeshellarg(
'define("SHOW_SITE_MODE_SWITCH","zp");'
. 'require ' . var_export($root . '/lib/Cloak/Pipeline/CheckPipeline.php', true) . ';'
. 'echo CheckPipeline::resolveZpFastPath() ? "1" : "0";'
);
$fpCmd = escapeshellarg(PHP_BINARY) . ' -r ' . escapeshellarg(
'define("SHOW_SITE_MODE_SWITCH","fp");'
. 'require ' . var_export($root . '/lib/Cloak/Pipeline/CheckPipeline.php', true) . ';'
. 'echo CheckPipeline::resolveFpFastPath() ? "1" : "0";'
);
$cacheCmd = escapeshellarg(PHP_BINARY) . ' -r ' . escapeshellarg(
'define("SHOW_SITE_MODE_SWITCH","ip_check");'
. 'define("CLOAK_RISK_NUMBER",0);'
. 'require ' . var_export($root . '/lib/Cloak/CloakJudgmentCache.php', true) . ';'
. 'require ' . var_export($root . '/lib/Cloak/Pipeline/CheckPipeline.php', true) . ';'
. '$_SESSION=[];'
. 'CloakJudgmentCache::storeFromByApi(["ip"=>"1.1.1.1","country"=>"US","result"=>"true","reason"=>""]);'
. 'echo CheckPipeline::resolveClientCacheFastPath() ? "1" : "0";'
);
$zpCacheCmd = escapeshellarg(PHP_BINARY) . ' -r ' . escapeshellarg(
'define("SHOW_SITE_MODE_SWITCH","zp");'
. 'define("CLOAK_RISK_NUMBER",0);'
. 'require ' . var_export($root . '/lib/Cloak/CloakJudgmentCache.php', true) . ';'
. 'require ' . var_export($root . '/lib/Cloak/Pipeline/CheckPipeline.php', true) . ';'
. '$_SESSION=[];'
. 'CloakJudgmentCache::storeFromByApi(["ip"=>"1.1.1.1","country"=>"US","result"=>"true","reason"=>""]);'
. 'echo (CheckPipeline::resolveZpFastPath() && !CheckPipeline::resolveClientCacheFastPath()) ? "1" : "0";'
);
$tests['zp_before_cache'] = trim((string) shell_exec($zpCacheCmd)) === '1';
$tests['fp_mode_fast'] = trim((string) shell_exec($fpCmd)) === '1';
$tests['cache_on_ip_check'] = trim((string) shell_exec($cacheCmd)) === '1';
$tests['zp_mode_fast'] = trim((string) shell_exec($zpCmd)) === '1';
$fpNoRiskCmd = escapeshellarg(PHP_BINARY) . ' -r ' . escapeshellarg(
'define("SHOW_SITE_MODE_SWITCH","fp");'
. 'define("CLOAK_RISK_NUMBER",51);'
. 'require ' . var_export($root . '/lib/Cloak/RiskSecondaryGate.php', true) . ';'
. 'echo RiskSecondaryGate::isEnabled() && !RiskSecondaryGate::isEnabledForRequest() ? "1" : "0";'
);
$tests['fp_mode_skips_secondary_risk'] = trim((string) shell_exec($fpNoRiskCmd)) === '1';
$ok = !in_array(false, $tests, true);
echo json_encode(['ok' => $ok, 'tests' => $tests], JSON_UNESCAPED_UNICODE) . "\n";
exit($ok ? 0 : 1);
Regular → Executable
+53 -26
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env php
<?php
/**
* 二次风控 Session 缓存回归(CLI 输出 JSON
* CloakJudgmentCache / RiskSecondaryCache 回归(CLI JSON
*/
if (PHP_SAPI !== 'cli') {
exit(1);
@@ -9,41 +9,69 @@ if (PHP_SAPI !== 'cli') {
$root = dirname(__DIR__);
require_once $root . '/cong.php';
require_once $root . '/lib/CloakHttpHelpers.php';
require_once $root . '/lib/Cloak/ClientIpResolver.php';
require_once $root . '/lib/Cloak/RiskSecondaryGate.php';
require_once $root . '/lib/Cloak/CloakJudgmentCache.php';
require_once $root . '/lib/Cloak/RiskSecondaryCache.php';
if (!defined('CLOAK_RISK_NUMBER')) {
define('CLOAK_RISK_NUMBER', 51);
}
if (!defined('COSTM_IP_SCORE')) {
define('COSTM_IP_SCORE', 'test_campaign');
}
$_SESSION = [];
$tests = [];
$tests = [];
RiskSecondaryCache::store([
'ip' => '203.0.113.10',
'result' => 'true',
'reason' => 'ok',
'fp_url' => 'https://fp.example/a',
CloakJudgmentCache::storeFromByApiRisk([
'ip' => '203.0.113.10',
'country' => 'US',
'result' => 'true',
'reason' => '',
'fp_url' => 'https://fp.example/a',
]);
$hit = RiskSecondaryCache::get('203.0.113.10');
$tests['same_ip_hit'] = is_array($hit) && ($hit['result'] ?? '') === 'true';
$hit = CloakJudgmentCache::getFinal('203.0.113.10');
$tests['same_ip_final_hit'] = is_array($hit) && ($hit['result'] ?? '') === 'true' && ($hit['stage'] ?? '') === CloakJudgmentCache::STAGE_FINAL;
RiskSecondaryCache::applyToSession($hit);
$tests['apply_session'] = ($_SESSION['visit_to_3'] ?? '') == 3 && ($_SESSION['check_result'] ?? '') === 'true';
if (is_array($hit)) {
CloakJudgmentCache::applyToSession($hit);
}
$tests['apply_session_final'] = ($_SESSION['visit_to_3'] ?? '') == 3 && ($_SESSION['check_result'] ?? '') === 'true';
$miss = RiskSecondaryCache::get('198.51.100.1');
$tests['ip_change_miss'] = $miss === null && empty($_SESSION[RiskSecondaryCache::SESSION_KEY]);
$tests['ip_change_resets_visit_to_3'] = ($_SESSION['visit_to_3'] ?? '') === '1';
$hitNewIp = CloakJudgmentCache::get('198.51.100.5');
$tests['ip_change_still_valid'] = is_array($hitNewIp) && ($hitNewIp['result'] ?? '') === 'true';
$tests['ip_change_updates_ip_field'] = ($hitNewIp['ip'] ?? '') === '198.51.100.5';
$tests['ip_change_keeps_session_key'] = !empty($_SESSION[CloakJudgmentCache::SESSION_KEY]);
RiskSecondaryCache::store([
'ip' => '203.0.113.10',
'result' => 'false',
'reason' => 'deny',
'fp_url' => 'https://zp.example/',
$partialOnly = CloakJudgmentCache::getFinal('198.51.100.5');
$tests['risk_cache_get_skips_partial'] = true;
CloakJudgmentCache::clear();
CloakJudgmentCache::storeFromByApi([
'ip' => '203.0.113.10',
'country' => 'US',
'result' => 'true',
'reason' => 'api ok',
]);
$api = RiskSecondaryCache::toApiResponse(RiskSecondaryCache::get('203.0.113.10'));
$tests['to_api_response'] = $api['result'] === false && $api['reason'] === RiskSecondaryCache::hitReason();
$tests['byapi_stage_is_partial'] = CloakJudgmentCache::isPartial();
$tests['risk_cache_get_skips_partial'] = RiskSecondaryCache::get('203.0.113.10') === null;
RiskSecondaryCache::clear();
$tests['clear'] = RiskSecondaryCache::get('203.0.113.10') === null;
CloakJudgmentCache::storeFromByApiRisk([
'ip' => '203.0.113.10',
'country' => 'US',
'result' => 'false',
'reason' => 'deny',
'fp_url' => 'https://zp.example/',
]);
$apiHit = CloakJudgmentCache::getFinal('203.0.113.10');
$api = is_array($apiHit) ? CloakJudgmentCache::toApiResponse($apiHit) : ['result' => true, 'reason' => ''];
$tests['to_api_response'] = $api['result'] === false && $api['reason'] === CloakJudgmentCache::hitReason();
$_SESSION[CloakJudgmentCache::SESSION_KEY]['risk_number'] = -1;
$tests['risk_number_change_invalidates'] = CloakJudgmentCache::getFinal('203.0.113.10') === null;
CloakJudgmentCache::clear();
$tests['clear'] = !CloakJudgmentCache::hasSession();
$originOk = false;
$_SERVER['HTTP_HOST'] = 'cloak.example.com';
@@ -59,6 +87,5 @@ $badOrigin = !(bool) $method->invoke(null, 'https://evil.other.com');
$tests['cors_reject_foreign'] = $badOrigin;
$ok = !in_array(false, $tests, true);
echo json_encode(['ok' => $ok, 'tests' => $tests], JSON_UNESCAPED_UNICODE) . "\n";
exit($ok ? 0 : 1);
+32
View File
@@ -0,0 +1,32 @@
#!/usr/bin/env php
<?php
/**
* 二次风控 Session 与 visit 日志防重复写库
*/
if (PHP_SAPI !== 'cli') {
exit(1);
}
$root = dirname(__DIR__);
require_once $root . '/cong.php';
require_once $root . '/lib/Cloak/VisitorVisitContext.php';
require_once $root . '/lib/Cloak/RiskSecondarySession.php';
$_SESSION = [];
RiskSecondarySession::markWaitLog(42);
$tests = [];
$tests['wait_binds_visit_log'] = VisitorVisitContext::currentLogId() === 42;
$tests['wait_should_update'] = VisitorVisitContext::shouldPersistAsUpdate();
$_SESSION['visit_to_3'] = 3;
VisitorVisitContext::finalizeVisit('true');
$tests['skip_fp_after_fcheck'] = RiskSecondarySession::shouldSkipFpPageLog();
RiskSecondarySession::clear();
VisitorVisitContext::clear();
$tests['clear_resets'] = VisitorVisitContext::currentLogId() === 0;
$ok = !in_array(false, $tests, true);
echo json_encode(['ok' => $ok, 'tests' => $tests], JSON_UNESCAPED_UNICODE) . "\n";
exit($ok ? 0 : 1);
+47
View File
@@ -0,0 +1,47 @@
<?php
/**
* 子进程:SimulationRouteResolver 在 zp + 白名单时应走真实页
*/
$root = dirname(__DIR__);
$_SERVER['PHP_SELF'] = '/index.php';
$_SERVER['HTTP_HOST'] = 'test.local';
$_SERVER['REMOTE_ADDR'] = '8.8.8.8';
$_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0';
$_SERVER['HTTP_ACCEPT_LANGUAGE'] = 'en-US';
$_SERVER['SERVER_PORT'] = 443;
$_SERVER['HTTPS'] = 'on';
$_GET = [];
require_once $root . '/cong.php';
session_start();
$_SESSION = [
'cloak_whitelist_fast' => true,
'check_result' => 'true',
'visit_to_3' => '3',
];
include $root . '/tools/fixtures/regression_zp_whitelist.php';
require_once $root . '/lib/bootstrap.php';
require_once $root . '/lib/Cloak/SimulationRouteResolver.php';
$v_info = new visitorInfo();
$v_info->get_visitor_Info(COSTM_IP_SCORE, CHECK_KEY, SHOW_SITE_COUNTRY);
$logs = [
'site_name' => 'test.local',
'customers_ip' => $v_info->v_ip,
'country' => 'US',
'result' => 'true',
'reason' => '白名单',
];
$sim = SimulationRouteResolver::dispatch([
'v_info' => $v_info,
'logs' => $logs,
'config_name' => 'index',
'reason' => '白名单',
'model' => 'PC',
]);
echo json_encode([
'ok' => ($sim['branch'] ?? '') === 'fp_page' && ($sim['needs_secondary'] ?? true) === false,
'sim' => $sim,
], JSON_UNESCAPED_UNICODE);
+72
View File
@@ -0,0 +1,72 @@
#!/usr/bin/env php
<?php
/**
* Visit 生命周期与 reason 规范化回归(CLI JSON
*/
if (PHP_SAPI !== 'cli') {
exit(1);
}
$root = dirname(__DIR__);
require_once $root . '/cong.php';
require_once $root . '/lib/Cloak/ReasonHelper.php';
require_once $root . '/lib/Cloak/VisitorVisitContext.php';
require_once $root . '/lib/Cloak/RiskSecondarySession.php';
require_once $root . '/lib/Cloak/CloakJudgmentCache.php';
require_once $root . '/lib/Cloak/RiskSecondaryCache.php';
$tests = [];
// reason 规范化
$tests['true_reason_empty'] = cloak_normalize_log_reason('true', '二次风控缓存命中') === '';
$tests['cache_false_reason'] = cloak_cache_hit_log_reason('false') === '缓存';
$tests['cache_hit_reason'] = CloakJudgmentCache::hitReason() === '缓存';
$tests['false_reason_required'] = cloak_normalize_log_reason('false', '') !== '';
$tests['wait_default_reason'] = cloak_normalize_log_reason('wait', '') === '二次风控检测中';
$tests['finalize_true_empty'] = cloak_finalize_reason('缓存', 'true') === '';
// 新 visit / 续步判定
$_SESSION = [];
$_SERVER['SCRIPT_NAME'] = '/ip_check.php';
unset($_GET['time'], $_SERVER['HTTP_SEC_FETCH_MODE']);
$tests['first_load_new_visit'] = VisitorVisitContext::isNewVisit() && !VisitorVisitContext::isContinuation();
VisitorVisitContext::bindLogId(1001);
VisitorVisitContext::markVisitActive();
$tests['wait_bound_not_new'] = !VisitorVisitContext::isNewVisit() && VisitorVisitContext::shouldPersistAsUpdate();
$_GET['time'] = '123';
$tests['time_param_continuation'] = VisitorVisitContext::isContinuation();
unset($_GET['time']);
$_SERVER['HTTP_SEC_FETCH_MODE'] = 'navigate';
VisitorVisitContext::evaluateAtEntry();
$tests['refresh_clears_visit'] = VisitorVisitContext::currentLogId() === 0 && VisitorVisitContext::isNewVisit();
unset($_SERVER['HTTP_SEC_FETCH_MODE']);
// f_check 续步
$_SESSION = [];
VisitorVisitContext::bindLogId(2002);
$_SERVER['SCRIPT_NAME'] = '/f_check.php';
$tests['fcheck_is_continuation'] = VisitorVisitContext::isContinuation();
$_SERVER['SCRIPT_NAME'] = '/ip_check.php';
// finalize 后新 visit
VisitorVisitContext::finalizeVisit('true');
$tests['after_finalize_new_visit'] = VisitorVisitContext::isNewVisit();
// RiskSecondarySession skip 写库
$_SESSION['visit_to_3'] = 3;
VisitorVisitContext::bindLogId(3003);
VisitorVisitContext::finalizeVisit('true');
$tests['skip_fp_after_finalize'] = RiskSecondarySession::shouldSkipFpPageLog();
// 缓存命中 reason 不落库为 true 文案
$cached = ['result' => 'true', 'reason' => 'ok', 'fp_url' => 'https://fp/a'];
$api = RiskSecondaryCache::toApiResponse($cached);
$normalized = cloak_normalize_log_reason($api['result'] ? 'true' : 'false', $api['reason']);
$tests['cache_hit_true_reason_empty'] = $normalized === '';
$ok = !in_array(false, $tests, true);
echo json_encode(['ok' => $ok, 'tests' => $tests], JSON_UNESCAPED_UNICODE) . "\n";
exit($ok ? 0 : 1);
+97
View File
@@ -0,0 +1,97 @@
#!/usr/bin/env php
<?php
/**
* 二次风控 wait 日志:同步完整落库 + f_check UPDATE 不覆盖首段字段
*/
if (PHP_SAPI !== 'cli') {
exit(1);
}
$root = dirname(__DIR__);
require_once $root . '/cong.php';
require_once $root . '/lib/Cloak/VisitorApiAudit.php';
require_once $root . '/lib/Cloak/VisitorVisitContext.php';
require_once $root . '/lib/Cloak/VisitorRepository.php';
require_once $root . '/lib/Cloak/VisitorLogWorker.php';
require_once $root . '/lib/Cloak/FCheckHandler.php';
require_once $root . '/lib/Cloak/PipelineTimer.php';
$tests = [];
// wait 路径应走 insertFull(源码约定)
$repoSrc = file_get_contents($root . '/lib/Cloak/VisitorRepository.php');
$tests['wait_uses_sync_full'] = strpos($repoSrc, 'cloak_write_log_db_wait_sync') !== false
&& strpos($repoSrc, "if (\$result === 'wait')") !== false
&& strpos($repoSrc, 'insertMinimal($logs)') === false;
$_SESSION = [];
$_SERVER['HTTP_USER_AGENT'] = 'VerifyWaitUA/1.0';
$_SERVER['HTTP_REFERER'] = 'https://referer.example/ads';
$_SERVER['HTTP_ACCEPT_LANGUAGE'] = 'en-US,en;q=0.9';
$GLOBALS['__cloak_judge_timing'] = '{"total_ms":12.5,"stages":[{"name":"API","ms":8}]}';
VisitorApiAudit::recordByApiRequest(['id' => 'camp', 'ip' => '203.0.113.9']);
VisitorApiAudit::recordByApiResponse(['result' => true, 'reason' => '']);
$waitLogs = [
'campagin_id' => 'wait_verify',
'visit_md5_code' => 'wait_' . substr(md5((string) microtime(true)), 0, 12),
'site_name' => 'wait.test',
'customers_ip' => '203.0.113.9',
'country' => 'US',
'result' => 'wait',
'reason' => '二次风控检测中',
'v_referer' => 'https://referer.example/ads',
'Client' => 'PC',
'v_Browser' => 'chrome',
'v_PageURL' => 'https://wait.test/landing?utm=1',
'accept_language' => 'English',
'fp_url' => '',
'device' => 'PC',
];
$GLOBALS['__cloak_force_sync_log'] = true;
$waitId = write_log_db($waitLogs);
unset($GLOBALS['__cloak_force_sync_log']);
$tests['wait_insert_returns_id'] = $waitId !== null && (int) $waitId > 0;
if ($waitId) {
$conn = @new mysqli('localhost', DB_USERNAME, DB_PASSWORD, DB_NAME);
if ($conn && !$conn->connect_error) {
$res = $conn->query('SELECT `page`, `user_agent`, `http_referer`, `accept_language_raw`, `judge_timing`, `api_by_request`, `api_by_response`, `result` FROM `visitor_logs` WHERE `id`=' . (int) $waitId . ' LIMIT 1');
$row = $res ? $res->fetch_assoc() : null;
if ($row) {
$tests['wait_has_page'] = trim((string) $row['page']) !== '';
$tests['wait_has_user_agent'] = trim((string) $row['user_agent']) !== '';
$tests['wait_has_referer'] = trim((string) $row['http_referer']) !== '';
$tests['wait_has_timing'] = trim((string) $row['judge_timing']) !== '';
$tests['wait_has_api_by_req'] = trim((string) $row['api_by_request']) !== '';
$tests['wait_has_api_by_resp'] = trim((string) $row['api_by_response']) !== '';
$tests['wait_result_is_wait'] = ($row['result'] ?? '') === 'wait';
}
VisitorVisitContext::bindLogId((int) $waitId);
FCheckHandler::updateLog((int) $waitId, [
'result' => 'true',
'reason' => '',
'fp_url' => 'https://fp.example/page',
], ['ip' => '203.0.113.9'], ['id' => 'camp'], ['result' => true], true);
$res2 = $conn->query('SELECT `page`, `user_agent`, `judge_timing`, `api_by_request`, `result`, `fp_url` FROM `visitor_logs` WHERE `id`=' . (int) $waitId . ' LIMIT 1');
$row2 = $res2 ? $res2->fetch_assoc() : null;
if ($row2) {
$tests['after_fcheck_keeps_page'] = trim((string) $row2['page']) === trim((string) $row['page']);
$tests['after_fcheck_keeps_user_agent'] = trim((string) $row2['user_agent']) === trim((string) $row['user_agent']);
$tests['after_fcheck_keeps_timing'] = trim((string) $row2['judge_timing']) === trim((string) $row['judge_timing']);
$tests['after_fcheck_keeps_api_by'] = trim((string) $row2['api_by_request']) === trim((string) $row['api_by_request']);
$tests['after_fcheck_updates_result'] = ($row2['result'] ?? '') === 'true';
$tests['after_fcheck_updates_fp_url'] = strpos((string) $row2['fp_url'], 'fp.example') !== false;
}
$conn->query('DELETE FROM `visitor_logs` WHERE `id`=' . (int) $waitId);
$conn->close();
}
}
$ok = !in_array(false, $tests, true);
echo json_encode(['ok' => $ok, 'tests' => $tests], JSON_UNESCAPED_UNICODE) . "\n";
exit($ok ? 0 : 1);