Excel zošit môže niesť obrázky EMF a WMF a konvenčný spôsob, ako jeden nakresliť, je odovzdať bajtový stream prehrávaču metafilov operačného systému. Toto je rozhodnutie, ktoré stojí za priamy pohľad: metafile je serializovaný príkazový stream pre grafické API a jeho prehratie znamená nechať súbor, ktorý prišiel e-mailom, riadiť grafický ovládač. HotXLS volí druhú cestu. XLSDecodeVectorScene parsuje metafile sám, validuje hlavičku, každú veľkosť záznamu, deklarovaný celkový počet záznamov a presné umiestnenie záznamu end-of-file, odmieta escape záznamy úplne a vracia TXLSVectorScene primitívnych kresliacich príkazov, ktoré backendy Canvas a SVG prehrávajú vlastným kódom. Žiadne prehrávanie ovládačom nie je zapojené v nijakom momente
Obchod je pokrytie za uzavretie. Whitelist príkazov orientovaný na obdĺžniky nezreprodukuje každý metafile, ktorý dokáže návrhár vytvoriť, takže scéna hlási, koľko kresliacich záznamov nedokázala reprezentovať, a volajúci rozhodne, čo s tým. Pre serverový proces renderujúci dokumenty, ktoré nevytvoril, je tento obchod správnym smerom
Prečo je prehrávanie metafilov zlý fit pre nedôveryhodný vstup?
Pretože formát nie je obrázok, je to program. Stream záznamov EMF manipuluje so zásobníkom stavov device context, alokuje a vyberá objekty z tabuľky handlov a môže niesť escape záznamy, ktorých užitočné zaťaženie sa odovzdá ovládaču zariadenia. Jeho prehratie precvičuje cesty v platformovom grafickom stacke napísané s predpokladom, že metafile prišiel od spolupracujúcej aplikácie na tom istom stroji. Keď je vstupom príloha tabuľky, ten predpoklad je preč a žiadna starostlivosť vnútri knižnice tabuliek nepomôže, pretože knižnica nie je komponenta, ktorá parsuje
Toto je to isté uvažovanie, ktoré riadi vrstvu kontajnera. Zošit je archív ZIP a HotXLS validuje jeho central directory namiesto dôvery deklarovaným offsetom, ako popisuje článok o validácii ZIP end-of-central-directory. Užitočné zaťaženia metafilov sú ďalšou vrstvou toho istého problému
Čo dekóder skontroluje, skôr než čokoľvek nakreslí
Validácia je štrukturálna a deje sa vopred, pretože parser, ktorý začne kresliť a validuje po ceste, už konal nad dátami, ktoré neoveril. Hlavička sa musí zhodovať prísne, nie pravdepodobne. Každý záznam musí deklarovať veľkosť, ktorá sa zmestí do zostávajúceho buffera a je dostatočne veľká pre vlastné pevné polia. Počet záznamov, ktorý hlavička deklaruje, sa musí zhodovať so záznamami skutočne prítomnými. Záznam end-of-file musí sedieť presne tam, kde stream končí, nie len niekde blízko, čo zatvára trik s koncovým odpadom, ktorý skrýva druhé užitočné zaťaženie za platným obrázkom
Nad rámec štruktúry je dekóder fail-closed na sémantike. Escape záznamy sú odmietnuté, nie preskočené. Záznam meniaci stav, ktorý dekóder nemodeluje, spôsobí zlyhanie dekódovania namiesto ignorovania, pretože ignorovanie zmeny stavu znamená, že každý následný kresliaci príkaz sa vykoná v stave, o ktorý súbor nežiadal, a výsledok je obrázok zlý spôsobom, ktorý nikto nedokáže predpovedať. Kresliace záznamy mimo podporovanej sady príkazov sú iná vec: tie sa počítajú a preskakujú, pretože chýbajúci tvar je viditeľná, reportovateľná medzera, nie tiché poškodenie
Rozpočty sú súčasťou kontraktu formátu
Vektorové formáty majú vlastnú verziu dekompresnej bomby. Pár kilobajtov záznamov môže deklarovať polylínie so stovkami miliónov bodov alebo obraz, ktorej deklarované rozmery sa vynásobia do terabajtov. Hranice preto musia byť explicitné konštanty, nie to, čo stroj náhodou prežije
// Z lxVectorScene: rozpočet dekódovania, vyjadrený, nie naznačený
XL_VECTOR_MAX_RECORDS = 1000000;
XL_VECTOR_MAX_HANDLES = 4096;
XL_VECTOR_MAX_DC_DEPTH = 32;
XL_VECTOR_MAX_COMMANDS = 100000;
XL_VECTOR_MAX_POINTS_PER_RECORD = 100000;
XL_VECTOR_MAX_TOTAL_POINTS = 2000000;
XL_VECTOR_MAX_TEXT_CHARS = 4096;
XL_VECTOR_MAX_TOTAL_TEXT_CHARS = 1000000;
XL_VECTOR_MAX_IMAGE_SIDE = 8192;
XL_VECTOR_MAX_IMAGE_PIXELS = 32 * 1024 * 1024;
XL_VECTOR_MAX_IMAGE_BYTES = 64 * 1024 * 1024;
XL_VECTOR_MAX_COORD = 1000000000;
Dva z nich si zaslúžia poznámku. Hĺbkový strop device context 32 existuje, pretože záznamy SaveDC a RestoreDC sa hniezdia a nevyvážený stream môže tlačiť naveky; 32 je štedré pre reálne metafile a lacné na vynútenie. Strop súradníc existuje, pretože súradnice kŕmia transformáciu a hodnota blízko limitov celočíselného rozsahu produkuje transformovaný výsledok, ktorý je buď nekonečný alebo sa pretočí, po čom je každý výpočet ohraničujúceho rámca po prúde nesmysl. Obmedzenie súradníc v čase parsovania je oveľa ľahšie rozmysliteľné než bránenie každého konzumenta geometrie
Použitie scény
Dekóder odovzdá späť objekt, ktorý vlastníte, počet príkazov, nominálnu veľkosť a počet kresliacich záznamov, ktoré sa rozhodol nereprezentovať
uses
lxVectorScene;
var
Scene: TXLSVectorScene;
Error: WideString;
I: Integer;
begin
// Data nesie surové užitočné zaťaženie obrázka prevzaté zo zošitu
if not XLSDecodeVectorScene(Data, xlsvfEmf, Scene, Error) then
begin
// Odmietnuté: hlavička, hranice, súčty, umiestnenie EOF alebo rozpočet
LogReject('metafile rejected: ' + Error);
Exit;
end;
try
if Scene.SkippedDrawRecords > 0 then
LogWarning(Format('%d drawing records outside the safe subset',
[Scene.SkippedDrawRecords]));
for I := 0 to Scene.Count - 1 do
case Scene.Commands[I].Kind of
xlsvcRectangle: DrawRect(Scene.Commands[I]);
xlsvcEllipse: DrawEllipse(Scene.Commands[I]);
xlsvcPolyline,
xlsvcPolygon,
xlsvcBezier: DrawPath(Scene.Commands[I]);
xlsvcText: DrawText(Scene.Commands[I]);
xlsvcImage: DrawImage(Scene.Commands[I]);
end;
finally
Scene.Free;
end;
end;
Záznam príkazu nesie všetko, čo backend potrebuje, a nič, čo vyžaduje zariadenie: prítomnosť pera, farbu, šírku a štýl; prítomnosť a farbu štetca; geometriu; a pri texte reťazec, meno fontu, veľkosť, štýly a zarovnanie. To je to, čo robí tú istú scénu použiteľnou on-screen canvas rendererom aj SVG writerom, a je to dôvod, prečo sa vektorová cesta neodchýli medzi náhľadom a exportom. Renderovanie obsahu zošitu na obrazovke všeobecne pokrýva článok o renderovaní vlastnej VCL mriežky
Odmietnutie obrázka nepoškodí zošit
Dôležitá vlastnosť tohto návrhu je, že odmietnuté dekódovanie ovplyvňuje len renderovanie. Pôvodné užitočné zaťaženie zostáva v modeli, takže zošit, ktorý sa otvorí a znovu uloží, vynesie svoje obrázky metafilov bajt po bajte, či už ich bezpečný dekóder dokázal nakresliť alebo nie. Existujúca ohraničená rastrová cesta zostáva tiež dostupná ako fallback. Inými slovami, prísny parser stráži to, čo sa vykoná, nie to, čo sa uchová, čo je rozlíšenie, ktoré nechá bezpečnostne motivovanú zmenu vyjsť bez premeny na zmenu so stratou dát
Zaobchádzanie s kreslenými objektmi všeobecne, vrátane častí objektového modelu, ktoré prežívajú obojsmerné prechody nedotknuté, pokrýva článok o grafoch, obrázkoch a kresbách
Kde tým zostáva serverové nasadenie
Ak renderujete používateľmi nahrané zošity v službe, praktická pozícia je teraz obhájiteľná: obrázky metafilov parsuje kód, ktorý môžete audítovať, ohraničený konštantami, ktoré môžete čítať, a nikdy sa neodovzdá grafickému ovládaču. Úprimná výhrada je pokrytie. Zložité metafile vyrobené kresliacimi nástrojmi zasiahnu počítadlo preskočených záznamov a odpoveďou je vyniesť počítadlo na povrch, nie potichu rozširovať whitelist. Obrázok, ktorý sa vykreslí čiastočne a povie to, je rozhovor s podporou; obrázok, ktorý sa vykreslí zle a nič nepovie, je hlásenie chyby od zákazníka
HotXLS spracováva XLS, XLSX, ODS a CSV natívne v Delphi a C++Builder bez nainštalovaného Excelu a tá istá filozofia ohraničeného parsovania prebieha jej vrstvami kontajnera, vzorcov a kreslenia. Detaily formátu a bezpečnosti sú uvedené na produktovej stránke HotXLS Delphi spreadsheet component