From ce6fa974aa1446c1cc7a4da998dc7b4aff35db1a Mon Sep 17 00:00:00 2001 From: pavlov346346-source Date: Mon, 7 Sep 2026 19:31:00 +0300 Subject: [PATCH] =?UTF-8?q?Caterium=20v17.6.3=20=E2=80=94=20fix=20develope?= =?UTF-8?q?r=20gate=20and=20SaaS=20settings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix platform developer gate during workspace loading and prevent SaaS click events from reaching Developer Console as DOM buttons. Add v17.6.3 regression tests and PWA cache bump. --- package.json | 4 +-- public/core/hotfix-v1763.js | 71 +++++++++++++++++++++++++++++++++++++ public/core/performance.js | 14 +++++--- public/service-worker.js | 6 ++-- tests/app.spec.mjs | 26 ++++++++++++++ tests/release-check.mjs | 9 +++-- tests/static-security.mjs | 8 +++-- 7 files changed, 122 insertions(+), 16 deletions(-) create mode 100644 public/core/hotfix-v1763.js diff --git a/package.json b/package.json index 520c5e1..6040cb8 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,10 @@ { "name": "caterium-app", "private": true, - "version": "17.6.2", + "version": "17.6.3", "type": "module", "scripts": { - "check:syntax": "node --check public/app-runtime.js && node --check public/service-worker.js && node --check public/legacy/bootstrap.js && node --check public/core/sun-safe.js && node --check public/core/performance.js && node --check public/core/ops-ux-v1762.js && node --check public/core/pdf-engine.js", + "check:syntax": "node --check public/app-runtime.js && node --check public/service-worker.js && node --check public/legacy/bootstrap.js && node --check public/core/sun-safe.js && node --check public/core/performance.js && node --check public/core/hotfix-v1763.js && node --check public/core/ops-ux-v1762.js && node --check public/core/pdf-engine.js", "test:static": "node tests/static-security.mjs", "check:release": "node tests/release-check.mjs", "check:deploy": "npm run check:syntax && npm run test:static && npm run check:release", diff --git a/public/core/hotfix-v1763.js b/public/core/hotfix-v1763.js new file mode 100644 index 0000000..ed4e13e --- /dev/null +++ b/public/core/hotfix-v1763.js @@ -0,0 +1,71 @@ +(()=>{ + 'use strict'; + if(window.SunHotfixV1763)return; + + const VERSION='17.6.3'; + const $=id=>document.getElementById(id); + const esc=value=>window.SunSafe?.escapeHTML?window.SunSafe.escapeHTML(String(value??'')):String(value??'').replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c])); + let checkingGate=false; + + function patchDeveloperOpen(){ + const dev=window.SunDeveloperV22; + if(!dev||dev.__sunV1763SafeOpen||typeof dev.open!=='function')return false; + const original=dev.open.bind(dev); + dev.open=(source=null)=>original(source instanceof HTMLElement?source:null); + Object.defineProperty(dev,'__sunV1763SafeOpen',{value:true,configurable:true}); + return true; + } + + async function platformAdmin(){ + const dev=window.SunDeveloperV22,cloud=window.SunCloudV2; + if(!dev||!cloud?.getSession?.()?.user)return false; + if(dev.isPlatformAdmin?.()===true)return true; + try{return await dev.checkPlatformAdmin?.(true)===true}catch(_){return false} + } + + async function enhanceDeveloperGate(){ + if(checkingGate)return false; + const cloud=window.SunCloudV2,dev=window.SunDeveloperV22; + if(!cloud||!dev)return false; + const session=cloud.getSession?.(),workspace=cloud.getWorkspace?.(); + if(!session?.user||workspace)return false; + checkingGate=true; + try{ + if(!await platformAdmin())return false; + const gate=$('sunCloudAuthGateV3'),card=gate?.querySelector('.sun-cloud-auth-card'); + if(!gate||!card)return false; + if(card.dataset.dev1763==='1')return true; + gate.dataset.saasEnhanced='1'; + card.dataset.devEnhanced='1'; + card.dataset.dev1763='1'; + card.innerHTML=`
Caterium

Аккаунт разработчика

${esc(session.user.email||'')}

Этот аккаунт управляет платформой и не обязан иметь собственную рабочую компанию.

`; + card.querySelector('[data-open-dev-v1763]')?.addEventListener('click',()=>{patchDeveloperOpen();dev.open?.();}); + card.querySelector('[data-signout-v1763]')?.addEventListener('click',()=>cloud.signOut?.()); + return true; + }finally{checkingGate=false;} + } + + function interceptSaasAdmin(event){ + const button=event.target?.closest?.('[data-saas-admin]'); + if(!button)return; + const dev=window.SunDeveloperV22; + if(!dev)return; + event.preventDefault(); + event.stopImmediatePropagation(); + patchDeveloperOpen(); + dev.open?.(); + } + + function maintain(){ + patchDeveloperOpen(); + enhanceDeveloperGate().catch(()=>{}); + } + + document.addEventListener('click',interceptSaasAdmin,true); + window.addEventListener('sun:cloud-permissions-changed',()=>setTimeout(maintain,0)); + window.addEventListener('sun:cloud-state-applied',()=>setTimeout(maintain,0)); + setTimeout(maintain,0); + setInterval(()=>{if(!document.hidden)maintain();},1000); + + window.SunHotfixV1763={VERSION,patchDeveloperOpen,enhanceDeveloperGate}; +})(); diff --git a/public/core/performance.js b/public/core/performance.js index 68cf4dc..ab7af74 100644 --- a/public/core/performance.js +++ b/public/core/performance.js @@ -53,7 +53,7 @@ const input=e.target;if(!(input instanceof HTMLInputElement)||input.id!=='sunChatFilesV29'||guardedInputs.has(input)||typeof DataTransfer==='undefined')return; e.preventDefault();e.stopImmediatePropagation();guardChatFiles(input).catch(err=>{console.error('[Caterium photo compression]',err);guardedInputs.add(input);input.dispatchEvent(new Event('change',{bubbles:true}));guardedInputs.delete(input);}); },true); - window.SunAttachmentGuard={VERSION:'17.6.2',MAX_FILE,TARGET,MAX_SIDE,compressImage,prepareAttachment}; + window.SunAttachmentGuard={VERSION:'17.6.3',MAX_FILE,TARGET,MAX_SIDE,compressImage,prepareAttachment}; let memoryData=null,memoryAt=0,memoryLoading=false; const esc=v=>window.SunSafe?.escapeHTML?window.SunSafe.escapeHTML(String(v??'')):String(v??''); @@ -65,23 +65,27 @@ async function enhanceDeveloperMemory(){ const view=document.getElementById('sun-developer-console-v22');if(!view?.classList.contains('on'))return; const body=document.getElementById('sunDevBody');if(!body)return; - const version=document.getElementById('sunDevReleaseVersion');if(version)version.textContent='17.6.2'; + const version=document.getElementById('sunDevReleaseVersion');if(version)version.textContent='17.6.3'; const d=await loadMemory();if(!d||!view.classList.contains('on'))return; let box=document.getElementById('sunDevMemoryV1761');if(!box){box=document.createElement('div');box.id='sunDevMemoryV1761';box.className='sun-dev-grid';box.style.marginBottom='12px';body.prepend(box);} box.innerHTML=`
Память сервера${esc(d.server_size||d.database_size||'—')}
База PostgreSQL${esc(d.database_size||'—')}
Файлы Storage${esc(d.storage_size||'—')}
Объектов Storage${Number(d.storage_objects||0)}
`; } + function loadHotfix(){ + if(window.SunHotfixV1763||document.getElementById('sunHotfixV1763Script'))return; + const script=document.createElement('script');script.id='sunHotfixV1763Script';script.src='core/hotfix-v1763.js?v=20260907-v17-6-3-developer-hotfix';script.async=true;script.onerror=()=>console.error('[Caterium] Не загрузился модуль hotfix-v1763.js');document.head.appendChild(script); + } function loadOpsUX(){ if(window.SunOpsUXV1762||document.getElementById('sunOpsUXV1762Script'))return; - const script=document.createElement('script');script.id='sunOpsUXV1762Script';script.src='core/ops-ux-v1762.js?v=20260907-v17-6-2-ops-ux';script.async=true;script.onerror=()=>console.error('[Caterium] Не загрузился модуль ops-ux-v1762.js');document.head.appendChild(script); + const script=document.createElement('script');script.id='sunOpsUXV1762Script';script.src='core/ops-ux-v1762.js?v=20260907-v17-6-3-developer-hotfix';script.async=true;script.onerror=()=>console.error('[Caterium] Не загрузился модуль ops-ux-v1762.js');document.head.appendChild(script); } const start=()=>{ - loadOpsUX();scan(document); + loadHotfix();loadOpsUX();scan(document); const mo=new MutationObserver(records=>{records.forEach(r=>r.addedNodes.forEach(n=>{if(n.nodeType===1)scan(n)}));enhanceDeveloperMemory();}); mo.observe(document.documentElement,{childList:true,subtree:true}); document.addEventListener('click',e=>{if(e.target.closest('#sunDeveloperNavV22,[data-dev-tab],#sunDevRefresh'))setTimeout(enhanceDeveloperMemory,100)},true); window.addEventListener('sun:cloud-state-applied',()=>setTimeout(enhanceDeveloperMemory,150)); setInterval(()=>{if(document.getElementById('sun-developer-console-v22')?.classList.contains('on'))enhanceDeveloperMemory();},5000); - window.SunPerformance={scanImages:()=>scan(document),refreshDeveloperMemory:enhanceDeveloperMemory,loadOpsUX,disconnect:()=>mo.disconnect()}; + window.SunPerformance={scanImages:()=>scan(document),refreshDeveloperMemory:enhanceDeveloperMemory,loadHotfix,loadOpsUX,disconnect:()=>mo.disconnect()}; }; if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',start,{once:true});else start(); })(); diff --git a/public/service-worker.js b/public/service-worker.js index 5962654..80beccd 100644 --- a/public/service-worker.js +++ b/public/service-worker.js @@ -1,8 +1,8 @@ -const CACHE='sun-catering-pwa-v67-20260907-v17-6-2-ops-ux'; -const VERSION='20260907-v17-6-2-ops-ux'; +const CACHE='sun-catering-pwa-v68-20260907-v17-6-3-developer-hotfix'; +const VERSION='20260907-v17-6-3-developer-hotfix'; const CORE=[ './','./index.html', - `./core/sun-safe.js?v=${VERSION}`,`./core/performance.js?v=${VERSION}`,`./core/ops-ux-v1762.js?v=${VERSION}`,`./core/pdf-engine.js?v=${VERSION}`,`./legacy/bootstrap.js?v=${VERSION}`,`./app-runtime.js?v=${VERSION}`, + `./core/sun-safe.js?v=${VERSION}`,`./core/performance.js?v=${VERSION}`,`./core/hotfix-v1763.js?v=${VERSION}`,`./core/ops-ux-v1762.js?v=${VERSION}`,`./core/pdf-engine.js?v=${VERSION}`,`./legacy/bootstrap.js?v=${VERSION}`,`./app-runtime.js?v=${VERSION}`, './offer-gallery/001.jpg','./offer-gallery/002.jpg', './catalog/001.jpg','./catalog/002.jpg','./catalog/003.jpg', './sun-logo.png','./caterium-login-logo.png','./pwa-icon-192.png','./pwa-icon-512.png','./manifest.webmanifest', diff --git a/tests/app.spec.mjs b/tests/app.spec.mjs index aef0992..08e22e1 100644 --- a/tests/app.spec.mjs +++ b/tests/app.spec.mjs @@ -65,6 +65,32 @@ test('v17.6.2 operations UX boots with menu and route features', async ({ page } expect(checks.calendarMore).toBeTruthy(); expect(await page.locator('header nav button', {hasText:'Меню'}).count()).toBeGreaterThan(0); }); +test('v17.6.3 developer gate bypasses workspace loading and SaaS click is safe', async ({ page }) => { + await page.goto('/index.html', { waitUntil:'domcontentloaded' }); + await page.evaluate(()=>{ + document.body.innerHTML='

Загружаю рабочую базу

'; + window.__devOpenArgs=[]; + window.SunCloudV2={ + getSession:()=>({user:{id:'dev',email:'developer@example.com'}}), + getWorkspace:()=>null, + status:()=>({membershipsLoading:true,membershipsLoaded:false}), + signOut:()=>{} + }; + window.SunDeveloperV22={ + open:arg=>{window.__devOpenArgs.push(arg===null?'null':arg?.constructor?.name||typeof arg)}, + isPlatformAdmin:()=>true, + checkPlatformAdmin:async()=>true + }; + }); + const content=fs.readFileSync(path.join(process.cwd(),'public','core','hotfix-v1763.js'),'utf8'); + await page.addScriptTag({content}); + await page.waitForFunction(()=>document.querySelector('[data-open-dev-v1763]')&&Boolean(window.SunHotfixV1763),null,{timeout:5000}); + expect(await page.locator('#sunCloudAuthGateV3 h2').textContent()).toBe('Аккаунт разработчика'); + await page.locator('[data-saas-admin]').click(); + await page.locator('[data-open-dev-v1763]').click(); + const args=await page.evaluate(()=>window.__devOpenArgs); + expect(args).toEqual(['null','null']); +}); test('mobile body does not overflow viewport', async ({ page }, testInfo) => { test.skip(testInfo.project.name!=='mobile-390'); await page.goto('/index.html', { waitUntil:'domcontentloaded' }); await page.waitForTimeout(500); const dims=await page.evaluate(()=>({innerWidth,scrollWidth:document.documentElement.scrollWidth,bodyWidth:document.body.scrollWidth})); diff --git a/tests/release-check.mjs b/tests/release-check.mjs index 91636f0..aeb2fed 100644 --- a/tests/release-check.mjs +++ b/tests/release-check.mjs @@ -3,16 +3,19 @@ import path from 'node:path'; const root=process.cwd(), pub=path.join(root,'public'); const read=p=>fs.readFileSync(path.join(pub,p),'utf8'); let bad=0;const check=(v,m)=>{console.log(`${v?'OK':'FAIL'}: ${m}`);if(!v)bad++}; -const index=read('index.html'),runtime=read('app-runtime.js'),sw=read('service-worker.js'),css=read('core/stability-v1760.css'),performance=read('core/performance.js'),ops=read('core/ops-ux-v1762.js'); +const index=read('index.html'),runtime=read('app-runtime.js'),sw=read('service-worker.js'),css=read('core/stability-v1760.css'),performance=read('core/performance.js'),ops=read('core/ops-ux-v1762.js'),hotfix=read('core/hotfix-v1763.js'); check(index.includes('core/stability-v1760.css'),'mobile stability stylesheet loaded'); check(css.includes('overflow-x:hidden')&&css.includes('.cats'),'mobile overflow guard present'); check(!index.includes('offer-gallery-data.js'),'blocking Base64 gallery absent'); check((runtime.match(/\/Type \/Catalog/g)||[]).length===0,'runtime contains no PDF binary writer'); check(read('core/pdf-engine.js').includes('595.28')&&read('core/pdf-engine.js').includes('841.89'),'PDF engine uses A4 MediaBox'); check([...index.matchAll(/@page\{([^}]*)\}/g)].every(m=>/size:A4/i.test(m[1])),'compact @page rules use A4'); -check(sw.includes('v17-6-2-ops-ux')&&sw.includes('ops-ux-v1762.js'),'service worker cache is v17.6.2'); +check(sw.includes('v17-6-3-developer-hotfix')&&sw.includes('hotfix-v1763.js'),'service worker cache is v17.6.3'); check(performance.includes('SunAttachmentGuard')&&performance.includes('TARGET=2*1024*1024'),'chat photo auto-compression is versioned'); check(performance.includes("rpc('sun_dev_dashboard')")&&performance.includes('server_size')&&performance.includes('storage_size'),'Developer Console server/storage counters are versioned'); +check(performance.includes('hotfix-v1763.js')&&performance.includes('SunHotfixV1763'),'developer/SaaS hotfix is loaded'); +check(hotfix.includes('patchDeveloperOpen')&&hotfix.includes('enhanceDeveloperGate')&&hotfix.includes('data-saas-admin'),'developer gate and SaaS click hotfix is versioned'); +check(hotfix.includes('source instanceof HTMLElement')&&hotfix.includes('stopImmediatePropagation'),'SaaS event object cannot reach Developer Console as a nav button'); check(performance.includes('ops-ux-v1762.js'),'operations UX module is loaded by performance core'); check(ops.includes('supportReadPermission')&&ops.includes('SUPPORT_POLL_MS=12000'),'developer support read-only live refresh is versioned'); check(ops.includes('sun-menu-editor-v1762')&&ops.includes('Премиум боксы'),'full-page menu editor is versioned'); @@ -20,5 +23,5 @@ check(ops.includes('showCalendarDay')&&ops.includes('.cal-more'),'calendar overf check(ops.includes("ROUTE_BASE_KEY='sunRouteBaseV1'")&&ops.includes('saveRouteStart'),'configurable route origin is versioned'); check(ops.includes('showRouteOrder')&&ops.includes('data-route-open'),'route order modal is versioned'); check(fs.existsSync(path.join(root,'supabase/functions/caterium-create-employee/index.ts')),'employee Edge Function source is versioned'); -check(!/sb_secret_[A-Za-z0-9_-]{20,}|service_role\s*[:=]\s*["'][A-Za-z0-9._-]{30,}/i.test(index+runtime+performance+ops),'no client secret-like token'); +check(!/sb_secret_[A-Za-z0-9_-]{20,}|service_role\s*[:=]\s*["'][A-Za-z0-9._-]{30,}/i.test(index+runtime+performance+ops+hotfix),'no client secret-like token'); if(bad)process.exit(1); diff --git a/tests/static-security.mjs b/tests/static-security.mjs index 4a20c73..5b9f8a5 100644 --- a/tests/static-security.mjs +++ b/tests/static-security.mjs @@ -6,7 +6,7 @@ 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'); +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'); 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 ['${b.name}','${o.event}','${o.address||','value="${x[0]}"','value="${x[2]}"']) if(legacy.includes(raw)) fail(`legacy bootstrap contains raw HTML interpolation ${raw}`); @@ -27,10 +27,12 @@ if(current!==113)fail(`current catalog photo count ${current}, expected 113`);el 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-2-ops-ux')||!sw.includes('ops-ux-v1762.js')||sw.includes('offer-gallery-data.js'))fail('service worker cache is stale');else ok('PWA cache updated'); +if(!sw.includes('v17-6-3-developer-hotfix')||!sw.includes('hotfix-v1763.js')||sw.includes('offer-gallery-data.js'))fail('service worker cache is stale');else ok('PWA cache updated'); 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('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=12000','supportReadPermission','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}`); -if(/service_role\s*[:=]\s*['"][A-Za-z0-9._-]{20,}/i.test(ops)||/eyJ[a-zA-Z0-9_-]{30,}/.test(ops))fail('possible secret in ops UX module');else ok('ops UX module has no hard-coded secret'); +if(/service_role\s*[:=]\s*['"][A-Za-z0-9._-]{20,}/i.test(ops+hotfix)||/eyJ[a-zA-Z0-9_-]{30,}/.test(ops+hotfix))fail('possible secret in ops/hotfix module');else ok('ops/hotfix modules have no hard-coded secret'); if(process.exitCode)process.exit(process.exitCode);