caterium-app/tests/static-security.mjs
pavlov346346-source 71d9cdc92e
Caterium v17.6.5 stability hardening
* Prepare v17.6.5 stability hardening patch

* Move v17.6.5 stability patch logic to script

* Fix v17.6.5 stability patch workflow

* Harden Caterium v17.6.5 stability

* Remove one-time v17.6.5 patch workflow

* Remove one-time v17.6.5 patch script

* Update static stability checks for v17.6.5

* Fix v17.6.5 E2E version expectation

* Update v17.6.5 E2E version expectation

* Remove one-time v17.6.5 test patch workflow

* Capture failing v17.6.5 E2E diagnostics

* Capture v17.6.5 E2E failures

* Make v17.6.5 stability E2E deterministic

* Make v17.6.5 E2E checks deterministic

* Remove one-time deterministic E2E workflow

* Remove one-time E2E diagnostic workflow

* Remove temporary E2E diagnostics

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-08 05:55:48 +03:00

46 lines
6.8 KiB
JavaScript

import fs from 'node:fs';
import path from 'node:path';
const root=process.cwd();
const pub=path.join(root,'public');
const readPub=p=>fs.readFileSync(path.join(pub,p),'utf8');
const readRoot=p=>fs.readFileSync(path.join(root,p),'utf8');
const fail=m=>{console.error('FAIL:',m);process.exitCode=1};
const ok=m=>console.log('OK:',m);
const html=readPub('index.html'),legacy=readPub('legacy/bootstrap.js'),runtime=readPub('app-runtime.js'),safe=readPub('core/sun-safe.js'),sw=readPub('service-worker.js'),performance=readPub('core/performance.js'),ops=readPub('core/ops-ux-v1762.js'),hotfix=readPub('core/hotfix-v1763.js'),ux=readPub('core/ux-fixes-v1764.js');
if(!html.includes('core/sun-safe.js'))fail('SunSafe must load before legacy modules');else ok('shared SunSafe loaded');
if(html.includes('offer-gallery-data.js')||fs.existsSync(path.join(pub,'offer-gallery-data.js')))fail('blocking offer-gallery-data.js still present');else ok('base64 gallery removed');
for(const raw of ['<span>${b.name}</span>','<span>${o.event}</span>','<span>${o.address||','value="${x[0]}"','value="${x[2]}"']) if(legacy.includes(raw)) fail(`legacy bootstrap contains raw HTML interpolation ${raw}`);
if(!legacy.includes('sunEsc(')||!legacy.includes('sunAttr('))fail('legacy bootstrap is not using shared escaping');else ok('legacy bootstrap escapes text and attributes');
const duplicateEsc=[html,runtime].join('\n').split('\n').filter(line=>/\b(?:const|let)\s+esc\b/.test(line)&&line.includes('.replace('));
if(duplicateEsc.length)fail(`duplicate esc implementations remain: ${duplicateEsc.length}`);else ok('local esc aliases delegate to SunSafe');
if(runtime.includes('/Type /Catalog'))fail('manual PDF writer remains in app-runtime.js');else ok('PDF packing removed from runtime');
if(!readPub('core/pdf-engine.js').includes('/Type /Catalog'))fail('shared PDF engine missing');else ok('single shared PDF engine present');
const employeeEdge=path.join(root,'supabase/functions/caterium-create-employee/index.ts');
if(!fs.existsSync(employeeEdge))fail('production employee Edge Function source missing');else ok('production employee Edge Function versioned');
const edge=readRoot('supabase/functions/caterium-create-employee/index.ts');
if(/service_role\s*[:=]\s*['"][A-Za-z0-9._-]{20,}/i.test(edge)||/eyJ[a-zA-Z0-9_-]{30,}/.test(edge))fail('possible hard-coded secret in Edge Function');else ok('no hard-coded service key detected');
if(!safe.includes('reference.parentNode===parent'))fail('safe insertBefore guard missing');else ok('safe DOM insertion guard present');
const files=fs.readdirSync(path.join(pub,'catalog'));
const current=files.filter(x=>/^current-\d{3}\.jpg$/i.test(x)).length;
const legacyCount=files.filter(x=>/^\d{3}\.jpg$/i.test(x)).length;
if(current!==113)fail(`current catalog photo count ${current}, expected 113`);else ok('113 current catalog photos');
if(legacyCount!==60)fail(`legacy catalog photo count ${legacyCount}, expected 60`);else ok('60 legacy catalog photos');
const gallery=fs.readdirSync(path.join(pub,'offer-gallery')).filter(x=>/\.jpg$/i.test(x));
if(gallery.length!==2)fail(`offer gallery contains ${gallery.length} jpg files, expected 2`);else ok('offer gallery trimmed');
if(!sw.includes('v17-6-5-stability')||!sw.includes('ux-fixes-v1764.js')||sw.includes('offer-gallery-data.js'))fail('service worker cache is stale');else ok('PWA cache updated to v17.6.5');
if(html.includes('20260907-v17-6-0-stability-security')||!html.includes('20260908-v17-6-5-stability'))fail('index still serves stale core asset version');else ok('index cache-busting is current');
if(!performance.includes('SunAttachmentGuard')||!performance.includes('MAX_SIDE=2048'))fail('chat photo compression guard missing');else ok('chat photo compression guard present');
if(!performance.includes("rpc('sun_dev_dashboard')")||!performance.includes('storage_size')||!performance.includes('server_size'))fail('Developer Console memory counters missing');else ok('Developer Console memory counters present');
if(performance.includes('records.forEach(r=>r.addedNodes.forEach(n=>{if(n.nodeType===1)scan(n)}));enhanceDeveloperMemory()'))fail('Developer Console memory refresh is still coupled to MutationObserver');else ok('Developer Console memory refresh loop removed');
if(!performance.includes('MEMORY_REFRESH_MS=30000')||!performance.includes('MEMORY_TIMEOUT_MS=8000')||!performance.includes('memoryPromise'))fail('Developer Console bounded refresh controls missing');else ok('Developer Console bounded refresh controls present');
if(!performance.includes('pendingImageRoots')||!performance.includes('queueImageScan'))fail('batched image mutation scanning missing');else ok('image mutation scanning is batched');
if(!performance.includes('ux-fixes-v1764.js')||!performance.includes('SunUXFixV1764'))fail('v17.6.4 UX fix loader missing');else ok('v17.6.4 UX fix loader present');
if(!performance.includes('hotfix-v1763.js')||!performance.includes('SunHotfixV1763'))fail('v17.6.3 hotfix loader missing');else ok('v17.6.3 hotfix loader present');
if(!performance.includes('ops-ux-v1762.js')||!performance.includes('SunOpsUXV1762'))fail('ops UX loader missing');else ok('ops UX loader present');
for(const marker of ['patchDeveloperOpen','enhanceDeveloperGate','data-saas-admin','stopImmediatePropagation','instanceof HTMLElement']) if(!hotfix.includes(marker))fail(`developer/SaaS hotfix marker missing: ${marker}`);else ok(`developer/SaaS hotfix marker: ${marker}`);
for(const marker of ['SUPPORT_POLL_MS=20000','supportReadPermission','refreshSupportWorkspace','sun-menu-editor-v1762','showCalendarDay',"ROUTE_BASE_KEY='sunRouteBaseV1'",'showRouteOrder','routeOpenYandex']) if(!ops.includes(marker)) fail(`ops UX marker missing: ${marker}`); else ok(`ops UX marker: ${marker}`);
for(const marker of ["AUTO_DELAY_MS=60*1000","order.prepayment=total","order.status='Отдан заказчику'",'sunAutoCompletedAt','classificationDate','persistOfferTemplate','clientOfferTemplateId','offerTemplateId','sun-v1764-menu-icon']) if(!ux.includes(marker))fail(`v17.6.4 UX marker missing: ${marker}`);else ok(`v17.6.4 UX marker: ${marker}`);
for(const marker of ['CLOUD_RPC_TIMEOUT_MS=12000','CLOUD_CONFLICT_MAX_RETRIES=4','refreshSupportWorkspace',"const VERSION = '17.6.5'",'ERROR_DEDUPE_MS=5*60*1000','DEV_ADMIN_TTL_MS=30000']) if(!runtime.includes(marker))fail(`v17.6.5 stability marker missing: ${marker}`);else ok(`v17.6.5 stability marker: ${marker}`);
if(!hotfix.includes('checkPlatformAdmin?.(false)')||!hotfix.includes('},10000);'))fail('Developer fallback polling is still aggressive');else ok('Developer fallback polling is throttled');
if(/service_role\s*[:=]\s*['"][A-Za-z0-9._-]{20,}/i.test(ops+hotfix+ux)||/eyJ[a-zA-Z0-9_-]{30,}/.test(ops+hotfix+ux))fail('possible secret in ops/hotfix/ux module');else ok('ops/hotfix/ux modules have no hard-coded secret');
if(process.exitCode)process.exit(process.exitCode);