caterium-app/tests/static-security.mjs
pavlov346346-source 195e0ba5c3 fix: reconcile version/test drift from the unreviewed production merge
The just-merged production-only commits were never run through QA
(they were pushed directly to the production branch), so several
version markers and hardcoded test strings had drifted out of sync
with each other:

- package.json was bumped to 17.8.0 but package-lock.json,
  release-manifest.json and app-runtime.js's own VERSION constant were
  never updated to match - reverted to 17.7.3 since no other release
  artifact actually changed.
- service-worker.js's cache name legitimately moved to
  v81-20260912-account-center-loader (real new modules need the cache
  bust), but release-manifest.json's pwaCache field and two
  release-check.mjs assertions still expected the old v78 name.
- edge-security-v1774.mjs and static-security.mjs asserted the old
  employee role list (with "admin") and old PWA cache name that
  production's own commits had already changed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 14:40:22 +03:00

55 lines
9.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'),dataLayer=readPub('core/data-layer-v1773.js'),serverAutomation=readPub('core/server-automation-v1770.js'),ops=readPub('core/ops-ux-v1762.js'),hotfix=readPub('core/hotfix-v1763.js'),ux=readPub('core/ux-fixes-v1764.js'),classic=readPub('core/classic-offer-pdf-v1767.js'),developerUX=readPub('core/developer-console-v1768.js'),offerWorkspace=readPub('core/offer-workspace-v1769.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');
if(!legacy.includes("persistOrders('order.save')")||!legacy.includes("persistOrders('order.status')")||!legacy.includes("persistOrders('order.delete')")||!legacy.includes("persistCatalog('catalog.save')")||!legacy.includes("persistCatalog('catalog.delete')"))fail('legacy CRUD is not routed through data layer');else ok('legacy CRUD routes through data layer');
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('20260912-account-center-loader')||!sw.includes('login-signature-v1776.js')||!sw.includes('data-layer-v1773.js')||!sw.includes('server-automation-v1770.js')||!sw.includes('offer-workspace-v1769.js')||sw.includes('offer-gallery-data.js'))fail('service worker cache is stale');else ok('PWA cache updated for login refresh');
if(html.includes('20260907-v17-6-0-stability-security')||!html.includes('20260909-v17-7-3-clients-server-read')||!html.includes('classic-offer-pdf-v1767.js'))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('loadDataLayer')||!performance.includes('data-layer-v1773.js')||!performance.includes('loadServerAutomation'))fail('v17.7.1 data layer loaders missing');else ok('v17.7.1 data layer loaders present');
if(!dataLayer.includes("const VERSION='17.7.3'")||!dataLayer.includes('ordersRepo')||!dataLayer.includes('catalogRepo')||!dataLayer.includes('clientsRepo')||!dataLayer.includes('sun_v17_save_client_v1772')||!dataLayer.includes('sun_v17_clients_snapshot_v1773')||!dataLayer.includes('refreshServerClients')||!dataLayer.includes('CateriumDataV1772=api'))fail('v17.7.3 data layer missing');else ok('v17.7.3 data layer present');
if(!serverAutomation.includes("const VERSION='17.7.0'")||!serverAutomation.includes("rpc('sun_run_order_automation'")||!serverAutomation.includes('applyServerChanges'))fail('v17.7.0 server automation client missing');else ok('v17.7.0 server automation client present');
if(!performance.includes('ux-fixes-v1764.js')||!performance.includes('SunUXFixV1764'))fail('UX fix loader missing');else ok('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','CateriumServerAutomationV1770?.enabled']) if(!ux.includes(marker))fail(`UX compatibility marker missing: ${marker}`);else ok(`UX compatibility marker: ${marker}`);
for(const marker of ['CLOUD_RPC_TIMEOUT_MS=12000','CLOUD_CONFLICT_MAX_RETRIES=4','refreshSupportWorkspace',"const VERSION = '17.7.3'",'ERROR_DEDUPE_MS=5*60*1000','DEV_ADMIN_TTL_MS=30000']) if(!runtime.includes(marker))fail(`stability marker missing: ${marker}`);else ok(`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(!ux.includes('sunMenuIconV1766')||!ux.includes('sun-offer-template-mini-editorial-grid')||!runtime.includes('explicitTemplate'))fail('v17.6.6 proposal/menu markers missing');else ok('v17.6.6 proposal/menu markers present');
if(!classic.includes("const VERSION='17.6.7'")||!classic.includes('CLASSIC_IDS')||!classic.includes('ARCHIVE_IDS')||!classic.includes('renderPages'))fail('v17.6.7 classic PDF module missing');else ok('v17.6.7 classic PDF module present');
if(!developerUX.includes("const VERSION='17.6.8'")||!developerUX.includes('sun_dev_delete_company_v1768')||!developerUX.includes('sun_dev_error_groups_v1768')||!developerUX.includes('selectedAccountsWorkspace')||!developerUX.includes('sun-dev-plan-matrix')||!developerUX.includes('KNOWN_DOM_RACE'))fail('v17.6.8 Developer Console UX module missing');else ok('v17.6.8 Developer Console UX module present');
if(!offerWorkspace.includes("const VERSION='17.6.9'")||!offerWorkspace.includes('data-offer-workspace-tab=\"templates\"')||!offerWorkspace.includes('data-offer-gallery-slot')||!offerWorkspace.includes('sunOfferGalleryOverridesV1769'))fail('v17.6.9 offer workspace module missing');else ok('v17.6.9 offer workspace module present');
if(/service_role\s*[:=]\s*['"][A-Za-z0-9._-]{20,}/i.test(ops+hotfix+ux+classic+developerUX+offerWorkspace+dataLayer+serverAutomation)||/eyJ[a-zA-Z0-9_-]{30,}/.test(ops+hotfix+ux+classic+developerUX+offerWorkspace+dataLayer+serverAutomation))fail('possible secret in client modules');else ok('client modules have no hard-coded secret');
if(process.exitCode)process.exit(process.exitCode);