HotXLS odmieta smerovať 20 nebezpečných mien formul, vrátane CALL, REGISTER.ID, WEBSERVICE a DDE, do vašich Delphi user-function callbackov, pokiaľ si to explicitne nezapnete. Vlastnosť workbooku AllowUnsafeFormulaCallbacks má predvolené False, kontrola beží skôr, než sa vyhodnotí akýkoľvek argument, a odmietnuté volanie nahlási xlfeUnsafeFunctionDenied bez zavolania jediného handlera
Scenár, ktorý toto urobil nutným, je každodenný. Služba prijíma nahrané súbory XLS alebo XLSX, prepočíta ich na serveri a načíta späť pár súčtov. Host aplikácia si pred rokmi zaregistrovala handler OnUserFunction pre pár obchodných funkcií a niekde cestou tomu handleru vyrástla catch-all vetva, ktorá preposiela čokoľvek nerozpoznané do tabuľky pluginov. Nikto v tíme nikdy nenapísal =WEBSERVICE(...) do bunky. Uploader áno. Udržať tú formulu nedotknutú cez otvorenie, prepočet a uloženie je funkcia file fidelity. Pustiť ju kódu hostiteľa, ktorý dokáže otvoriť sockety alebo súbory, je autorizačné rozhodnutie a kým HotXLS tieto dve veci neseparoval, rozhodovala knižnica poticho za vás
Prečo sa zo zachovania formuly stalo povolenie ju spustiť?
Koreňom príčiny bola jediná fallback cesta. HotXLS parsuje každé meno Excel funkcie, ktoré pozná, ale nie každé známe meno má implementáciu v calculation engine. Built-iny rozpoznané, ale neimplementované padali do tej istej user-defined function fallback ako skutočne vlastné mená, takže CALL a REGISTER.ID zdieľali dispatch cestu s vašou DISCOUNT alebo REGIONRATE. Neznáme mená ako WEBSERVICE alebo DDE mohli obdobne sedieť na rovnomenný záznam v workbook registry, procesovo-globálnom registry alebo event handleri. Mechaniku tej fallback cesty pokrýva ako HotXLS rieši custom funkcie cez OnUserFunction; problém bolo, že nič na tej ceste sa nepýtalo, či to meno samo je niečo, čo by zdravý host mal kedykoľvek spustiť
Dispatch poradie má význam pre to, čo tu znamená „unknown“. Volanie, ktoré engine nedokáže vyhodnotiť natívne, sa ponúka postupne lexikálnym bindingom LAMBDA a LET, ktoré closure podpora vo formula engine HotXLS rieši ako prvé, potom workbook-local funkciám zaregistrovaným cez RegisterUserFunction, potom procesovo-globálnym funkciám z TXLSWorkbook.RegisterGlobalUserFunction a nakoniec eventom OnUserFunction a OnUserFunctionEx. Až keď všetky odmietnu, stáva sa skutočne neznáma funkcia #NAME?. Každá etapa po lambda vyhľadávaní podáva kontrolu do kódu, ktorý ste napísali vy, a presne preto musí safety kontrola sedieť pred celou reťazou, nie vnútri ktoréhokoľvek jedného handlera
Ktoré mená funkcií blokuje HotXLS defaultne?
XLSFormulaCallbackIsUnsafe v lxCalc.pas drží pevnú deny sadu 20 mien: DDE, CALL, REGISTER, REGISTER.ID, WEBSERVICE, RTD, SQL.REQUEST, EXEC, RUN, CREATE.OBJECT, APP.ACTIVATE, SEND.KEYS, OPEN, SAVE, SAVE.AS, FOPEN, FWRITE, FWRITELN, FCLOSE a FILE.DELETE. Sú to mená, ktoré v Exceli alebo jeho macro jazyku načítavajú natívny kód, siahajú na sieť, rozprávajú sa s inými procesmi alebo zahŕňajú súborový systém. Pred porovnaním funkcia ostrihá okolité whitespace, uppercasne meno a strhne jediný prefix _XLFN. alebo _XLWS., takže _xlfn.webservice napísaný novším buildom Excelu chytí rovnako ako holé hláskovanie. Zoznam žije na hranici kalkulačky, nie v parseroch Classic, XLSX a ODS, čo drží jeden AST, jeden BIFF token stream a jeden konvertovaný workbook správajúce sa identicky
Dve hrany stoja za poznanie, skôr než sa na to spoľahnete. Zhoda je exact, takže handler registrovaný ako MYWEBSERVICE nie je dotknutý a naopak legitímna interná UDF, ktorá sa náhodou volá OPEN alebo RUN, je teraz defaultne odmietaná. Deny sada tiež nie je sandbox pre vaše vlastné handlery. Ak vaša catch-all vetva spúšťa ľubovoľné plugin mená, brána zastaví tie slávne nebezpečné a nič viac; trvalá oprava je stále handler, ktorý matchuje explicitnú allowlist cez SameText a necháva Handled na False pre všetko, čo nevlastní
Prečo musí brána bežať skôr než vyhodnotenie argumentov?
Brána, ktorá vystrelí až po vypočítaní argumentov, je neskoro, pretože samotné argumenty môžu zavolať váš kód. GetValueItemUserFunction skontroluje meno ako prvé a vystúpi s lxErrorUnsafeFunctionDenied skôr, než postaví pole argumentov, skôr, než sa poradí s resolverom alebo ktorýmkoľvek registry, a dokonca skôr, než si vôbec všimne, že žiadny handler nie je priradený. Práve to poradie ničí vnorený prípad nižšie, kde by bolo vonkajšie volanie odmietnuté aj tak, ale neškodne vyzerajúca vnútorná UDF by inak vystrelila prvá a zanechala za sebou side effect
procedure TImportService.HandleUdf(Sender: TObject;
const FunctionName: WideString; const Args: Variant;
var Value: Variant; var Handled: Boolean);
begin
if SameText(FunctionName, 'AUDIT_TOKEN') then
begin
FAuditLog.Add('AUDIT_TOKEN evaluated'); // side effect v host kóde
Value := 'token-42';
Handled := True;
end;
end;
Book.OnUserFunction := HandleUdf;
Eval := Sheet.EvaluateFormulaAt(1, 1, '=WEBSERVICE(AUDIT_TOKEN())');
// Eval.Status = xlfeUnsafeFunctionDenied, Eval.Value = Null,
// Eval.Issue.NativeCode = -106 a FAuditLog je stále prázdny
Default workbooku verzus per-call TXLSFormulaEvaluationOptions
Flag workbooku je default a per-call option má posledné slovo. TXLSWorkbook.AllowUnsafeFormulaCallbacks a TXLSXWorkbook.AllowUnsafeFormulaCallbacks riadia obyčajný prepočet, Calculate, dvojargumentové EvaluateFormulaAt, evaluation šablóny, read-only viewy a na XLSX každého workera v paralelnom prepočtovom poole. Akýkoľvek vstupný bod, ktorý prijíma explicitný record TXLSFormulaEvaluationOptions, berie Options.AllowUnsafeFormulaCallbacks ako verdikt pre to volanie a neslepuje ho OR-om s vlastnosťou workbooku. Tá asymetria je zámerná: dôveryhodná interná úloha môže autorizovať jedno RTD vyhľadávanie bez preklopenia celého workbooku a workbook globálne optnutý in môže stále vnútiť citlivému vyhodnocovaniu späť odmietnutie
var
Options: TXLSFormulaEvaluationOptions;
Eval: TXLSFormulaEvaluationResult;
begin
// workbook zostáva zamknutý, jedno dôveryhodné volanie sa pustí
Book.AllowUnsafeFormulaCallbacks := False;
Options := XLSDefaultFormulaEvaluationOptions;
Options.AllowUnsafeFormulaCallbacks := True;
Eval := Sheet.EvaluateFormulaAt(4, 2, '=WEBSERVICE(B1)', xlfrsA1, Options);
// workbook optnutý in, ale toto vyhodnotenie nahraného textu nie
Book.AllowUnsafeFormulaCallbacks := True;
Options := XLSDefaultFormulaEvaluationOptions; // flag je znova False
Eval := Sheet.EvaluateFormulaAt(4, 2, UploadedFormula, xlfrsA1, Options);
if Eval.Status = xlfeUnsafeFunctionDenied then
LogRejected(Eval.Issue.Message);
end;
Prepnutie vlastnosti workbooku tiež označí graf závislostí ako dirty na oboch engineoch. Bez toho kroku by cached výsledok vypočítaný, kým boli callbacky povolené, mohol byť servírovaný po ich odobratí, alebo cached výsledok xlfeUnsafeFunctionDenied mohol prežiť opt-in. Nový status bol pripojený na koniec TXLSFormulaEvaluationStatus po xlfeFailed, takže má ordinal 10 a každý existujúci ordinal si drží svoju hodnotu; to isté pravidlo tail-append platí pre pole options recordu a getter a setter IXLSWorkbook, hoci konzument postavený proti staršiemu release stále potrebuje recompile
Čo sa stane s neznámym a nebezpečným textom formuly pri ukladaní?
Udržať formulu a spustiť ju sú teraz dve oddelené otázky a entry politika odpovedá len na prvú. FormulaEntryPolicy na ktorejkoľvek triede workbooku nesie UnknownFunctionMode a UnknownNameMode, obidve s defaultom xlfusmReject, takže priradenie formuly s neznámym volaním cez obyčajnú vlastnosť Formula sa odmietne skôr, než sa zmení hodnota bunky, formula cache alebo závislosti. ValidateFormulaEntry nahlási to isté rozhodnutie bez side effectov. Dôveryhodné cesty ako načítanie súboru, kopírovanie a konverzia formátu obchádzajú tú user-entry politiku, pretože prísny default nikdy nesmie odmietnuť symboly už prítomné v súbore, ktorý len otvárate
var
Policy: TXLSFormulaEntryPolicy;
begin
Policy := Book.FormulaEntryPolicy;
Policy.UnknownFunctionMode := xlfusmPreserve; // compatibility entry
Book.FormulaEntryPolicy := Policy;
Sheet.Cells[3, 1].Formula := '=ACME_RATE(B3)'; // uložené, neautorizované
Book.SaveAs('rates.xls');
end;
V Classic BIFF8 nemá neznáme volanie žiadny vlastný token, takže HotXLS ho zapisuje tak, ako Excel zapisuje add-in funkcie. Formula dostane token PtgNameX ($59), ktorého XTI entry ukazuje na add-in SUPBOOK s oboma sheet indexmi nastavenými na $FFFE, za ním nasledujú argument tokeny a PtgFuncVar nesúca funkčné číslo 255 a počet argumentov zahŕňajúci name slot. Záložné telo ExternName je šesť nulových bajtov, bajt dĺžky a Unicode flag, UTF-16 meno funkcie, potom dvojbajtová formula $1C $17, PtgErr držiaca #REF!. Writer odmietne mená dlhšie než 255 znakov, viac než 29 argumentov a cieľ BIFF5. Ako klasifikuje HotXLS tieto add-in SUPBOOK záznamy vedľa externých workbook linkov, vysvetľuje SUPBOOK a XTI klasifikačné pravidlá pre BIFF externé linky. XLSX drží surový text funkcie a ODS svoju formulu msoxl: a v každom formáte sa súbor, ktorý uložil =WEBSERVICE(...), znova otvorí s textom nedotknutým a stále sa vyhodnotí na xlfeUnsafeFunctionDenied defaultne
Ak váš pipeline vyhodnocuje workbooke, ktoré sám nenapísal, nechajte AllowUnsafeFormulaCallbacks na False, držte handlery na explicitnej allowliste a pripúšťajte per-call options len tam, kde zdroj formuly je váš. Kompletné callback, entry-policy a evaluation API je zdokumentované pri HotXLS Delphi spreadsheet component