Files
wordpress-theme-aksrt/tests/widget-system/site-security-contract.test.cjs
T

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, /\\AKSRT\\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|aksrt_sms_rate_|aksrt_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/aksrt-phone.php');
const bootstrap = read('inc/bootstrap.php');
const template = read('inc/helpers/user-center-renderers/settings-account.php');
assert.match(bootstrap, /inc\/helpers\/aksrt-phone\.php/);
assert.match(helper, /function aksrt_phone_mask_parts\(string \$phone\): \?array/);
assert.match(helper, /function aksrt_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, /aksrt_phone_mask_parts\(\$account_phone\)/);
assert.match(template, /aksrt_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/);
});