Files
wordpress-theme-aksrt/tests/widget-system/site-security-contract.test.cjs
T
kate522andClaude Opus 5 35bb96e82f refactor: rename the theme brand and every identifier from AKSRT to Jio
Full-project rename. `git grep -i aksrt` is now empty apart from the
DBMigrator 3.3.0 changelog note that documents the rename itself.

Code identifiers
- PHP namespace AKSRT\ -> Jio\ (composer PSR-4 + 378 declarations)
- global constants AKSRT_* -> JIO_* (VERSION/DIR/URI, PAY_TYPE_*, CAPTCHA_*)
- procedural functions, hooks, options, meta keys, table names,
  cookies, query vars, ajax actions and nonces: aksrt_ -> jio_
- text domain aksrt -> jio; Theme Name -> Jio
- JS globals: aksrtData/aksrtAdminData/... -> jioData/jioAdminData/...,
  window.AKSRTCaptcha & friends -> window.JioCaptcha, Aksrt* -> Jio*
- Vue components Aks*.vue -> Jio*.vue (15 files renamed + imports)
- CSS classes/data-attrs/custom properties: aksrt- -> jio-
- REST namespace aksrt/v1 -> jio/v1 (incl. payment gateway callbacks)

Storage layer
- DB tables {prefix}aksrt_* -> {prefix}jio_*; SCHEMA_VERSION 3.2.0 -> 3.3.0
  so dbDelta creates the new names on install
- widget id_base aksrt_widget_* -> jio_widget_*
- uninstall.php drop list rebuilt from the actual migrator tables
  (dropped 3 stale entries, added the 17 missing ones)

Removed
- hardcoded https://api.aksrt.com/version.json default update endpoint;
  get_endpoint() now reads the 「更新端点」 setting and do_check() no-ops
  when it is empty

Files renamed: inc/helpers/aksrt-*.php -> jio-*.php (54),
assets/admin/src/components/form/Aks*.vue -> Jio*.vue (15).
Build artifacts regenerated: assets/css/build, assets/js/runtime,
assets/admin/build.

Verified: audit-psr4-paths errors=0, audit-admin-contracts errors=0
(warnings=56, unchanged baseline), 813/813 widget-system tests,
44/44 phpunit, php -l clean across all PHP.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-11-01 20:05:04 +08:00

125 lines
6.9 KiB
JavaScript

'use strict';
const assert = require('node:assert/strict');
const { execFileSync } = require('node:child_process');
const fs = require('node:fs');
const path = require('node:path');
const test = require('node:test');
const root = path.resolve(__dirname, '..', '..');
const read = (file) => fs.readFileSync(path.join(root, file), 'utf8');
test('site security schema registers the 网站安全 page without 人机验证 fields', () => {
const schema = read('src/Admin/Schema/SchemaExtensionsSecurity.php');
const registry = read('src/Admin/Schema/SchemaExtensions.php');
const permissions = read('src/Admin/Schema/SchemaPermissions.php');
const clientMirror = read('assets/admin/src/schema/sections/extensions/security.js');
const clientIndex = read('assets/admin/src/schema/sections/extensions/index.js');
const nav = read('assets/admin/src/constants/sections/extensions.js');
assert.match(registry, /'security'\s+=> SchemaExtensionsSecurity::build\(\)/);
assert.match(schema, /'key'\s+=> 'security'/);
assert.match(schema, /'title'\s+=> '网站安全'/);
for (const key of [
'security_brush_post_10min', 'security_brush_post_day',
'security_brush_comment_10min', 'security_brush_comment_day',
'security_brush_upload_10min', 'security_brush_upload_day',
'security_brush_register_10min', 'security_brush_register_day',
'security_brush_order_10min', 'security_brush_order_day',
'security_captcha_send_cooldown', 'security_captcha_to_hour', 'security_captcha_to_day',
'security_captcha_ip_10min', 'security_captcha_ip_day',
'security_error_frequency_limit', 'security_disabled_pingback', 'security_disabled_xmlrpc',
'security_user_disable_admin', 'close_attachment_page',
]) {
assert.match(schema, new RegExp(`'${key}'`), `missing schema field ${key}`);
assert.match(clientMirror, new RegExp(`${key}`), `missing client mirror field ${key}`);
}
assert.doesNotMatch(schema + clientMirror, /verification_|geetest|tcaptcha|user_verification_type/);
assert.doesNotMatch(permissions, /close_attachment_page/);
assert.match(clientIndex, /import \{ securitySchema \} from '\.\/security\.js'/);
assert.match(clientIndex, /security: securitySchema/);
assert.match(nav, /key: 'extensions-security'/);
assert.match(nav, /route: '\/extensions\/security'/);
assert.match(nav, /section: 'security'/);
});
test('security settings are consumed at runtime and initialized on boot', () => {
const runtime = read('src/Settings/ExtensionRuntime.php');
const bootstrap = read('functions.php');
assert.match(runtime, /public static function get_security_settings\(\): array/);
assert.match(bootstrap, /\\Jio\\Extensions\\SecurityHardening::get_instance\(\)->init\(\)/);
});
test('hardening switches wire the documented WordPress hooks', () => {
const hardening = read('src/Extensions/SecurityHardening.php');
assert.match(hardening, /add_filter\('xmlrpc_enabled', '__return_false'\)/);
assert.match(hardening, /add_filter\('xmlrpc_methods', '__return_empty_array'\)/);
assert.match(hardening, /add_action\('pre_ping', \[\$this, 'disable_self_pingback'\]\)/);
assert.match(hardening, /add_action\('admin_init', \[\$this, 'restrict_admin_access'\]\)/);
assert.match(hardening, /wp_doing_ajax\(\) \|\| wp_doing_cron\(\)/);
assert.match(hardening, /current_user_can\('manage_options'\)/);
assert.match(hardening, /brush_allow\(string \$scope, string \$subject, int \$cap_10, int \$cap_day\)/);
assert.match(hardening, /error_frequency_wait\(\): int/);
assert.match(hardening, /error_frequency_record\(\): void/);
assert.match(hardening, /error_frequency_clear\(\): void/);
});
test('verification-code sends are funneled through the configurable send limiter', () => {
const sms = read('src/Sms/SMS.php');
const sendLimit = read('src/Extensions/SendLimit.php');
const ajax = read('inc/modules/user-parts/account-ajax.php');
const controller = read('src/Admin/UserControllerAuthPhoneOperations.php');
assert.match(sms, /SendLimit::check\(\$phone\)/);
assert.match(sms, /SendLimit::record\(\$phone\)/);
assert.doesNotMatch(sms, /RATE_LIMIT|DAILY_LIMIT|jio_sms_rate_|jio_sms_daily_/);
assert.match(sendLimit, /security_captcha_send_cooldown/);
assert.match(sendLimit, /security_captcha_to_hour/);
assert.match(sendLimit, /security_captcha_to_day/);
assert.match(sendLimit, /security_captcha_ip_10min/);
assert.match(sendLimit, /security_captcha_ip_day/);
assert.match(ajax, /SendLimit::check\(\$target\)/);
assert.match(ajax, /SendLimit::record\(\$target\)/);
assert.match(controller, /SendLimit::check\(\$target\)/);
assert.match(controller, /SendLimit::record\(\$target\)/);
});
test('content brush limits are wired into post, comment, upload, register, and order flows', () => {
const frontSubmit = read('src/Core/FrontSubmitSave.php');
const moderation = read('src/Auth/Moderation.php');
const upload = read('src/Attachment/UploadLimits.php');
const registerEmail = read('src/Auth/ServicePart1a.php');
const registerPhone = read('src/Auth/ServicePart1b.php');
const order = read('src/Payment/OrderRepositoryCore.php');
assert.match(frontSubmit, /brush_allow\('post', \$brush_subject, \(int\) \$security\['security_brush_post_10min'\], \(int\) \$security\['security_brush_post_day'\]\)/);
assert.match(moderation, /brush_allow\(\s*'comment'/);
assert.match(upload, /add_filter\('wp_handle_upload_prefilter', \[\$this, 'brush_limit_upload'\], 90\)/);
assert.match(upload, /brush_allow\('upload', \$subject, \(int\) \$security\['security_brush_upload_10min'\], \(int\) \$security\['security_brush_upload_day'\]\)/);
assert.match(registerEmail, /brush_allow\('register'/);
assert.match(registerPhone, /brush_allow\('register'/);
assert.match(order, /brush_allow\('order', \$brush_subject, \(int\) \$security\['security_brush_order_10min'\], \(int\) \$security\['security_brush_order_day'\]\)/);
});
test('phone masking is shared by the account UI and adapts to international numbers', () => {
const helper = read('inc/helpers/jio-phone.php');
const bootstrap = read('inc/bootstrap.php');
const template = read('inc/helpers/user-center-renderers/settings-account.php');
assert.match(bootstrap, /inc\/helpers\/jio-phone\.php/);
assert.match(helper, /function jio_phone_mask_parts\(string \$phone\): \?array/);
assert.match(helper, /function jio_phone_mask_display\(string \$phone\): string/);
assert.match(helper, /min\(11, max\(4, \$length - 7\)\)/);
assert.doesNotMatch(template, /substr_replace\(\$account_phone, '\*\*\*\*', 3, 4\)/);
assert.match(template, /jio_phone_mask_parts\(\$account_phone\)/);
assert.match(template, /jio_phone_mask_display\(\$account_phone\)/);
});
test('site security runtime contract harness passes', () => {
const harness = path.join(root, 'tests', 'widget-system', 'site-security-harness.php');
const output = execFileSync('php', [harness], { encoding: 'utf8' });
assert.match(output, /Site security harness passed/);
});