chore: save iqon static deploy snapshot
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
# IQON Phase 1 Hardening Package
|
||||
|
||||
Purpose: локальный пакет временного hardening для live WordPress `iqon.com` до полноценного редизайна или статической замены.
|
||||
|
||||
## Contents
|
||||
|
||||
- [Scope](#scope)
|
||||
- [Files](#files)
|
||||
- [Install Draft](#install-draft)
|
||||
- [Hosting Rule Draft](#hosting-rule-draft)
|
||||
- [Automated Verification Script](#automated-verification-script)
|
||||
- [Verification](#verification)
|
||||
- [Rollback](#rollback)
|
||||
- [Limits](#limits)
|
||||
|
||||
## Scope
|
||||
|
||||
Пакет не был установлен на live-сайт. Он подготовлен как reversible snippet для Code Snippets и должен применяться только после отдельного подтверждения на запись во внешний WordPress.
|
||||
|
||||
## Files
|
||||
|
||||
| File | Role |
|
||||
|------|------|
|
||||
| `wp-snippets/iqon-phase1-hardening.php` | PHP snippet for Code Snippets, "Run snippet everywhere" |
|
||||
| `wp-snippets/hosting-rules.md` | nginx/Apache/file-level drafts for install docs and XML-RPC |
|
||||
| `scripts/iqon-phase1-verify.sh` | Post-activation verification script |
|
||||
|
||||
## Install Draft
|
||||
|
||||
1. Открыть WordPress admin через текущий безопасный маршрут доступа.
|
||||
2. Создать новый snippet с названием `IQON Phase 1 hardening`.
|
||||
3. Вставить содержимое `wp-snippets/iqon-phase1-hardening.php`.
|
||||
4. Выбрать режим `Run snippet everywhere`.
|
||||
5. Сохранить snippet как inactive draft.
|
||||
6. Сделать свежий backup файлов и базы перед активацией.
|
||||
7. Активировать snippet.
|
||||
8. Сразу выполнить проверки из раздела Verification.
|
||||
|
||||
## Hosting Rule Draft
|
||||
|
||||
PHP snippet не гарантирует блокировку физических файлов, которые веб-сервер отдаёт до WordPress. Для `readme.html` и `license.html` нужен server-level блок или удаление файлов.
|
||||
|
||||
nginx draft:
|
||||
|
||||
```nginx
|
||||
location ~* ^/(readme|license)\.html$ {
|
||||
return 404;
|
||||
}
|
||||
```
|
||||
|
||||
Если на reg.ru shared hosting нет доступа к nginx-конфигу, проверить доступные механизмы: панель хостинга, `.htaccess`, файловый менеджер или удаление публичных install-doc файлов после backup.
|
||||
|
||||
Detailed drafts are in `wp-snippets/hosting-rules.md`.
|
||||
|
||||
## Automated Verification Script
|
||||
|
||||
After activation:
|
||||
|
||||
```bash
|
||||
scripts/iqon-phase1-verify.sh https://iqon.com
|
||||
```
|
||||
|
||||
Expected signal: final line is `[OK] iqon Phase 1 verification passed for https://iqon.com`.
|
||||
|
||||
## Verification
|
||||
|
||||
Run after activation:
|
||||
|
||||
```bash
|
||||
curl -I -L https://iqon.com/
|
||||
```
|
||||
|
||||
Expected signal: response includes `X-Content-Type-Options`, `X-Frame-Options`, `Referrer-Policy`, `Permissions-Policy`, and `Content-Security-Policy`.
|
||||
|
||||
```bash
|
||||
curl -sS https://iqon.com/wp-json/wp/v2/users
|
||||
```
|
||||
|
||||
Expected signal: no public list of usernames is returned.
|
||||
|
||||
```bash
|
||||
curl -sS https://iqon.com/ | rg 'http://www\.iqon\.com|http://iqon\.com|http://maps\.google\.com'
|
||||
```
|
||||
|
||||
Expected signal: no output.
|
||||
|
||||
```bash
|
||||
curl -sS https://iqon.com/ | rg 'name="viewport"|iqon-skip-link|og:site_name'
|
||||
```
|
||||
|
||||
Expected signal: viewport exists, skip link exists, site metadata no longer shows `- IQON`.
|
||||
|
||||
```bash
|
||||
curl -I -L https://iqon.com/readme.html
|
||||
```
|
||||
|
||||
Expected signal after hosting rule: `403` or `404`. If it still returns `200`, WordPress snippet is bypassed by static file serving and hosting-level action is still required.
|
||||
|
||||
```bash
|
||||
curl -I --http1.1 https://iqon.com/xmlrpc.php
|
||||
```
|
||||
|
||||
Expected signal: no usable XML-RPC behavior. A stronger check is a safe XML-RPC POST from a test client after confirming no production integrations depend on it.
|
||||
|
||||
## Rollback
|
||||
|
||||
1. Disable the `IQON Phase 1 hardening` snippet.
|
||||
2. Clear any page/cache layer if present.
|
||||
3. Re-run the same curl checks and compare before/after.
|
||||
4. Remove hosting rule only if it caused a confirmed false positive.
|
||||
|
||||
## Limits
|
||||
|
||||
- This package does not replace the contact form or legacy CAPTCHA.
|
||||
- This package does not fix duplicate sitemap URLs.
|
||||
- This package does not modernize the WordPress theme.
|
||||
- This package does not verify canonical contact data.
|
||||
- This package does not confirm the exact WordPress core version.
|
||||
|
||||
---
|
||||
|
||||
*Created: 20 May 2026*
|
||||
*Updated: 20 May 2026*
|
||||
@@ -0,0 +1,77 @@
|
||||
# IQON Phase 1 Hosting Rules
|
||||
|
||||
Purpose: зафиксировать server-level правила для рисков, которые WordPress snippet не может гарантированно закрыть.
|
||||
|
||||
## Contents
|
||||
|
||||
- [Scope](#scope)
|
||||
- [nginx Draft](#nginx-draft)
|
||||
- [Apache htaccess Draft](#apache-htaccess-draft)
|
||||
- [reg.ru Shared Hosting Path](#regru-shared-hosting-path)
|
||||
- [Verification](#verification)
|
||||
- [Rollback](#rollback)
|
||||
|
||||
## Scope
|
||||
|
||||
Эти правила не применены на live-сайте. Они являются draft-артефактом для внедрения после backup и явного подтверждения на remote write.
|
||||
|
||||
## nginx Draft
|
||||
|
||||
```nginx
|
||||
location ~* ^/(readme|license)\.html$ {
|
||||
return 404;
|
||||
}
|
||||
|
||||
location = /xmlrpc.php {
|
||||
return 403;
|
||||
}
|
||||
```
|
||||
|
||||
## Apache htaccess Draft
|
||||
|
||||
Use only if the hosting route confirms Apache or `.htaccess` processing for this site.
|
||||
|
||||
```apache
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
RewriteRule ^(readme|license)\.html$ - [R=404,L,NC]
|
||||
RewriteRule ^xmlrpc\.php$ - [F,L,NC]
|
||||
</IfModule>
|
||||
```
|
||||
|
||||
## reg.ru Shared Hosting Path
|
||||
|
||||
If neither nginx config nor `.htaccess` is available, use a file-level fallback after backup:
|
||||
|
||||
1. Confirm that `/readme.html` and `/license.html` are physical public files.
|
||||
2. Download or copy those files into the dated backup bundle.
|
||||
3. Remove the public files or replace them with non-sensitive 404 placeholders.
|
||||
4. Re-run verification commands below.
|
||||
|
||||
Do not overwrite server files without reading the destination first.
|
||||
|
||||
## Verification
|
||||
|
||||
```bash
|
||||
curl -I -L https://iqon.com/readme.html
|
||||
curl -I -L https://iqon.com/license.html
|
||||
curl -I --http1.1 https://iqon.com/xmlrpc.php
|
||||
```
|
||||
|
||||
Expected signal:
|
||||
|
||||
- `readme.html` returns `403` or `404`.
|
||||
- `license.html` returns `403` or `404`.
|
||||
- `xmlrpc.php` returns `403`, `404`, `405`, or another intentionally blocked status after confirming no integration depends on XML-RPC.
|
||||
|
||||
## Rollback
|
||||
|
||||
1. Remove only the rule that caused the confirmed regression.
|
||||
2. Restore public files only from the dated backup if file-level fallback was used.
|
||||
3. Re-run the same verification commands.
|
||||
4. Document the before/after status in the deployment notes.
|
||||
|
||||
---
|
||||
|
||||
*Created: 20 May 2026*
|
||||
*Updated: 20 May 2026*
|
||||
@@ -0,0 +1,223 @@
|
||||
<?php
|
||||
/**
|
||||
* IQON Phase 1 hardening snippet.
|
||||
*
|
||||
* Purpose: temporary WordPress hardening for iqon.com before the redesign
|
||||
* replaces or modernizes the legacy theme.
|
||||
*
|
||||
* Created: 2026-05-20
|
||||
* Updated: 2026-05-20
|
||||
* Deployment: Code Snippets plugin, "Run snippet everywhere".
|
||||
*
|
||||
* Safety:
|
||||
* - No credentials are stored here.
|
||||
* - This is reversible by disabling the snippet.
|
||||
* - Physical files such as /readme.html may be served before WordPress;
|
||||
* block those at the hosting/nginx layer for guaranteed enforcement.
|
||||
*/
|
||||
|
||||
if (!defined('ABSPATH')) {
|
||||
return;
|
||||
}
|
||||
|
||||
add_action('send_headers', 'iqon_phase1_send_security_headers', 100);
|
||||
add_action('wp_head', 'iqon_phase1_head_fallbacks', 1);
|
||||
add_filter('xmlrpc_enabled', '__return_false');
|
||||
add_filter('rest_endpoints', 'iqon_phase1_restrict_rest_users', 20);
|
||||
add_filter('the_generator', '__return_empty_string');
|
||||
add_filter('wpseo_opengraph_site_name', 'iqon_phase1_site_name');
|
||||
add_filter('wpseo_json_ld_output', 'iqon_phase1_rewrite_recursive_urls', 20);
|
||||
add_filter('wpseo_opengraph_image', 'iqon_phase1_rewrite_url_string', 20);
|
||||
add_action('template_redirect', 'iqon_phase1_install_doc_fallback_block', 0);
|
||||
add_action('template_redirect', 'iqon_phase1_start_output_buffer', 0);
|
||||
|
||||
function iqon_phase1_send_security_headers()
|
||||
{
|
||||
if (headers_sent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
header('X-Content-Type-Options: nosniff');
|
||||
header('X-Frame-Options: SAMEORIGIN');
|
||||
header('Referrer-Policy: strict-origin-when-cross-origin');
|
||||
header('Permissions-Policy: camera=(), microphone=(), geolocation=()');
|
||||
header(
|
||||
"Content-Security-Policy: default-src 'self' https: data:; " .
|
||||
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https://ajax.googleapis.com https://maps.google.com https://maps.googleapis.com; " .
|
||||
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; " .
|
||||
"img-src 'self' https: data:; " .
|
||||
"font-src 'self' https://fonts.gstatic.com data:; " .
|
||||
"frame-src https://www.google.com https://maps.google.com https://maps.googleapis.com; " .
|
||||
"connect-src 'self' https:; base-uri 'self'; frame-ancestors 'self'; upgrade-insecure-requests"
|
||||
);
|
||||
}
|
||||
|
||||
function iqon_phase1_head_fallbacks()
|
||||
{
|
||||
echo "\n" . '<style id="iqon-phase1-emergency-css">
|
||||
html { -webkit-text-size-adjust: 100%; }
|
||||
body { overflow-x: hidden; }
|
||||
a:focus-visible, button:focus-visible, input:focus-visible, textarea:focus-visible, select:focus-visible {
|
||||
outline: 3px solid #fbc100;
|
||||
outline-offset: 3px;
|
||||
}
|
||||
.iqon-skip-link {
|
||||
position: absolute;
|
||||
left: -999px;
|
||||
top: 8px;
|
||||
z-index: 9999;
|
||||
padding: 8px 12px;
|
||||
background: #000;
|
||||
color: #fff;
|
||||
}
|
||||
.iqon-skip-link:focus { left: 8px; }
|
||||
img, iframe { max-width: 100%; }
|
||||
@media (max-width: 980px) {
|
||||
#wrapper, #header, #menu, #header-image-home, #header-image, #breadcrumb, .download_case_file {
|
||||
width: auto !important;
|
||||
max-width: 960px;
|
||||
}
|
||||
#wrapper { margin-left: 12px !important; margin-right: 12px !important; }
|
||||
#menu { height: auto !important; min-height: 40px; background-repeat: repeat !important; }
|
||||
#menu a { float: none !important; display: block !important; padding: 0 16px !important; }
|
||||
.contentleft, .contentright, .homeleft, .homeright {
|
||||
float: none !important;
|
||||
width: auto !important;
|
||||
}
|
||||
.rowInput, .rowTextarea textarea, .rowInput input {
|
||||
width: 100% !important;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
</style>' . "\n";
|
||||
}
|
||||
|
||||
function iqon_phase1_restrict_rest_users($endpoints)
|
||||
{
|
||||
if (!is_array($endpoints)) {
|
||||
return $endpoints;
|
||||
}
|
||||
|
||||
foreach (array('/wp/v2/users', '/wp/v2/users/(?P<id>[\d]+)') as $route) {
|
||||
if (isset($endpoints[$route])) {
|
||||
unset($endpoints[$route]);
|
||||
}
|
||||
}
|
||||
|
||||
return $endpoints;
|
||||
}
|
||||
|
||||
function iqon_phase1_install_doc_fallback_block()
|
||||
{
|
||||
$request_uri = isset($_SERVER['REQUEST_URI']) ? (string) $_SERVER['REQUEST_URI'] : '';
|
||||
$path = parse_url($request_uri, PHP_URL_PATH);
|
||||
|
||||
if (!is_string($path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (preg_match('~/(readme|license)\.html$~i', $path)) {
|
||||
status_header(404);
|
||||
nocache_headers();
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
function iqon_phase1_start_output_buffer()
|
||||
{
|
||||
$doing_ajax = function_exists('wp_doing_ajax') ? wp_doing_ajax() : (defined('DOING_AJAX') && DOING_AJAX);
|
||||
|
||||
if (is_admin() || $doing_ajax || (defined('REST_REQUEST') && REST_REQUEST)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ob_start('iqon_phase1_rewrite_html');
|
||||
}
|
||||
|
||||
function iqon_phase1_rewrite_html($html)
|
||||
{
|
||||
if (!is_string($html) || $html === '') {
|
||||
return $html;
|
||||
}
|
||||
|
||||
$html = iqon_phase1_rewrite_url_string($html);
|
||||
|
||||
if (stripos($html, 'name="viewport"') === false && stripos($html, "name='viewport'") === false) {
|
||||
$html = preg_replace(
|
||||
'~</head>~i',
|
||||
'<meta name="viewport" content="width=device-width, initial-scale=1">' . "\n" . '</head>',
|
||||
$html,
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
if (stripos($html, 'class="iqon-skip-link"') === false) {
|
||||
$html = preg_replace(
|
||||
'~<body([^>]*)>~i',
|
||||
'<body$1><a class="iqon-skip-link" href="#content">Skip to content</a>',
|
||||
$html,
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
$html = preg_replace(
|
||||
'~(<img\b(?![^>]*\balt=)(?=[^>]*\b(?:homepage|header|logo|case)[^>]*)[^>]*)>~i',
|
||||
'$1 alt="">',
|
||||
$html
|
||||
);
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
function iqon_phase1_site_name()
|
||||
{
|
||||
return 'IQON';
|
||||
}
|
||||
|
||||
function iqon_phase1_rewrite_recursive_urls($value)
|
||||
{
|
||||
if (is_array($value)) {
|
||||
foreach ($value as $key => $item) {
|
||||
$value[$key] = iqon_phase1_rewrite_recursive_urls($item);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (is_object($value)) {
|
||||
foreach ($value as $key => $item) {
|
||||
$value->{$key} = iqon_phase1_rewrite_recursive_urls($item);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (is_string($value)) {
|
||||
return iqon_phase1_rewrite_url_string($value);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
function iqon_phase1_rewrite_url_string($value)
|
||||
{
|
||||
if (!is_string($value) || $value === '') {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return str_replace(
|
||||
array(
|
||||
'http://www.iqon.com',
|
||||
'http://iqon.com',
|
||||
'http://maps.google.com',
|
||||
'http://maps.googleapis.com',
|
||||
),
|
||||
array(
|
||||
'https://iqon.com',
|
||||
'https://iqon.com',
|
||||
'https://maps.google.com',
|
||||
'https://maps.googleapis.com',
|
||||
),
|
||||
$value
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user