HotXLS odklanja usmerjati 20 nevarnih imen formul, med njimi CALL, REGISTER.ID, WEBSERVICE in DDE, k Vašim povratnim klicem uporabniških funkcij v Delphiju, razen če tega ne vključite izrecno. Lastnost delovnega zvezka AllowUnsafeFormulaCallbacks ima privzeto False, preizkus teče, še preden se kateri koli argument ovrednoti, zavrnjen klic pa prijavi xlfeUnsafeFunctionDenied, ne da bi poklical en sam handler
Scenarij, ki je to nujno naredil, je vsakdanji. Storitev sprejme naložene datoteke XLS ali XLSX, preračuna jih na strežniku in prebere nekaj vsot nazaj. Gostiteljska aplikacija je pred leti registrirala handler OnUserFunction za par poslovnih funkcij, nekje na poti pa je ta handler zrasel vejo, ki ujame vse in karkoli ne prepozna, posreduje tabeli vtičnikov. Nihče v ekipi ni nikoli natipkal =WEBSERVICE(...) v celico. Naložil jo je uporabnik. Obdržati to formulo nedotaknjeno skozi odpiranje, preračun in shranjevanje je funkcija, ki varuje zvestobo datoteke. Pustiti ji, da doseže gostiteljsko kodo, ki lahko odpre vtičnice ali datoteke, pa je odločitev o pooblastilih — in dokler HotXLS teh dveh ni ločil, je knjižnica to odločitev tiho sprejemala za vas
Zakaj se je ohranjanje formule spremenilo v dovoljenje za njeno izvajanje?
Korenski vzrok je bila ena sama rezervna pot. HotXLS razčleni vsako ime funkcije Excel, ki ga pozna, a vsako znano ime nima implementacije v izračunskem pogonu. Vgrajene funkcije, prepoznane a neimplementirane, so se spuščale v isto rezervno pot za uporabniško definirane funkcije kot pravzaprav lastna imena, zato sta si CALL in REGISTER.ID delila pot razpošiljanja z Vašim DISCOUNT ali REGIONRATE. Neznana imena, kot sta WEBSERVICE ali DDE, so lahko prav tako zadela vnos z istim imenom v registru delovnega zvezka, procesnem registru ali dogodkovnem handlerju. Mehaniko te rezervne poti pokriva kako HotXLS razreši lastne funkcije prek OnUserFunction; težava je bila, da se nič na tej poti ni vprašalo, ali je ime samo tisto, ki ga pameten gostitelj sploh sme izvesti
Vrstni red razpošiljanja je pomemben za to, kaj tukaj pomeni »neznano«. Klic, ki ga pogon ne zna ovrednotiti domače, se po vrsti ponudi leksikalnim vezem LAMBDA in LET, ki jih najprej razreši podpora za closure v formulem pogonu HotXLS, nato funkcijam, lokalnim na delovnem zvezku, registriranim s RegisterUserFunction, nato funkcijam po vsem procesu iz TXLSWorkbook.RegisterGlobalUserFunction in končno dogodkoma OnUserFunction in OnUserFunctionEx. Šele, ko vse zavrnejo, postane resnično neznana funkcija #NAME?. Vsaka stopnja za iskanjem lambda predaja nadzor kodi, ki ste jo napisali Vi — točno zato mora varnostni preizkus sedeti pred celotno verigo in ne znotraj katerega koli posameznega handlerja
Katera imena funkcij HotXLS privzeto blokira?
XLSFormulaCallbackIsUnsafe v lxCalc.pas nosi fiksni niz 20 zavrnjenih imen: 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 in FILE.DELETE. To so imena, ki v Excelu ali njegovem makro jeziku nalagajo domačo kodo, dosežejo omrežje, govorijo z drugimi procesi ali se dotaknejo datotečnega sistema. Pred primerjavo funkcija odreže obkrožajoče presledke, ime pretvori v velike črke in odstrani enojno predpono _XLFN. ali _XLWS., zato je _xlfn.webservice, napisan s novejšo različico Excel, ujet tako kot gola oblika. Seznam živi na meji kalkulatorja in ne v razčlenjevalnikih Classic, XLSX in ODS, kar ohranja en AST, en tok žetonov BIFF in en pretvorjen delovni zvezek enakovestno obnašajoče
Dve ostrinki sta vredni poznavanja, preden se nanj zanesete. Ujemanje je točno, zato handler, ki ga registrirate kot MYWEBSERVICE, ni prizadet, po drugi strani pa je zakonita hišna UDF, ki slučajno nosi ime OPEN ali RUN, zdaj privzeto zavrnjena. Niz zavračanj tudi ni peskovnik za Vaše lastne handlerje. Če Vaša veja, ki ujame vse, izvaja poljubna imena vtičnikov, pregrada zaustavi slavna nevarna imena in nič drugega; trajen popravek je še vedno handler, ki se ujema z izrecnim seznamom dovoljenih prek SameText in za vse, česar ne lasti, pusti Handled na False
Zakaj mora pregrada teči pred ovrednotenjem argumentov?
Pregrada, ki sproži šele, ko so argumenti izračunani, je prepozno, ker lahko argumenti sami pokličejo Vašo kodo. GetValueItemUserFunction najprej preveri ime in izstopi z lxErrorUnsafeFunctionDenied, preden zgradi tabelo argumentov, preden se posvetuje z razreševalnikom ali katerim koli registrom, in celo preden opazi, da sploh ni dodeljen noben handler. Ta vrstni red je tisto, kar premaga gnezdeni primer spodaj, kjer bi bil zunanji klic vseeno zavrnjen, a bi neškodljivo videča notranja UDF sicer strelala prva in za seboj pustila svoj stranski učinek
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'); // stranski učinek v gostiteljski kodi
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, FAuditLog pa je še vedno prazen
Privzeta nastavitev delovnega zvezka proti TXLSFormulaEvaluationOptions na klic
Zastavica delovnega zvezka je privzeta, možnost na klic pa ima zadnjo besedo. TXLSWorkbook.AllowUnsafeFormulaCallbacks in TXLSXWorkbook.AllowUnsafeFormulaCallbacks vladata običajnemu preračunu, Calculate, dvoparametrskemu EvaluateFormulaAt, predlogam za ovrednotenje, pogledom samo za branje in, na XLSX, vsakemu delavcu v vzporednem bazenu preračunavanja. Vsaka vstopna točka, ki sprejme izrecni zapis TXLSFormulaEvaluationOptions, vzame Options.AllowUnsafeFormulaCallbacks kot sodbo za ta klic in je ne združuje (OR) z lastnostjo delovnega zvezka. Ta asimetrija je namerna: zaupanja vredno notranje opravilo lahko pooblasti en sam iskalni klic RTD, ne da bi preklopilo celoten delovni zvezek, delovni zvezek, globalno vključen, pa lahko še vedno občutljivo ovrednotenje prisili nazaj v zavrnitev
var
Options: TXLSFormulaEvaluationOptions;
Eval: TXLSFormulaEvaluationResult;
begin
// delovni zvezek ostane zaklenjen, en zaupanja vreden klic gre skozi
Book.AllowUnsafeFormulaCallbacks := False;
Options := XLSDefaultFormulaEvaluationOptions;
Options.AllowUnsafeFormulaCallbacks := True;
Eval := Sheet.EvaluateFormulaAt(4, 2, '=WEBSERVICE(B1)', xlfrsA1, Options);
// delovni zvezek je vključen, a to ovrednotenje naloženega besedila ni
Book.AllowUnsafeFormulaCallbacks := True;
Options := XLSDefaultFormulaEvaluationOptions; // zastavica je spet False
Eval := Sheet.EvaluateFormulaAt(4, 2, UploadedFormula, xlfrsA1, Options);
if Eval.Status = xlfeUnsafeFunctionDenied then
LogRejected(Eval.Issue.Message);
end;
Preklop lastnosti delovnega zvezka tudi označi graf odvisnosti kot umazan na obeh pogonih. Brez tega koraka bi lahko predpomnjen rezultat, izračunan, medtem ko so bili povratni klici dovoljeni, bil poslužen še potem, ko so bili odvzeti, ali pa bi predpomnjena izida xlfeUnsafeFunctionDenied preživela vključitev. Novo stanje je bilo dodano na konec TXLSFormulaEvaluationStatus za xlfeFailed, zato ima vrstilno vrednost 10 in vsaka obstoječa ohrani svojo; isto pravilo pripone na rep velja za polje zapisa možnosti ter getter in setter IXLSWorkbook, pri čemer porabnik, zgrajen proti starejši izdaji, kljub vsemu potrebuje ponovno prevajanje
Kaj se z neznanim in nevarnim besedilom formul zgodi ob shranjevanju?
Obdržati formulo in jo zagnati sta zdaj dve ločeni vprašanji, politika vnosov pa odgovarja le na prvo. FormulaEntryPolicy na obeh razredih delovnih zvezkov nosi UnknownFunctionMode in UnknownNameMode, oba privzeto xlfusmReject, zato je dodelitev formule z neznanim klicem prek običajne lastnosti Formula zavrnjena, še preden se spremeni vrednost celice, predpomnilnik formul ali odvisnosti. ValidateFormulaEntry sporoči isto odločitev brez stranskih učinkov. Zaupanja vredne poti, kot so nalaganje datotek, kopiranje in pretvorba formatov, to politiko uporabniških vnosov obidejo, ker stroga privzeta nastavitev nikoli ne sme zavrniti simbolov, ki so že prisotni v datoteki, ki jo le odpirate
var
Policy: TXLSFormulaEntryPolicy;
begin
Policy := Book.FormulaEntryPolicy;
Policy.UnknownFunctionMode := xlfusmPreserve; // vnos za združljivost
Book.FormulaEntryPolicy := Policy;
Sheet.Cells[3, 1].Formula := '=ACME_RATE(B3)'; // shranjeno, ne pooblaščeno
Book.SaveAs('rates.xls');
end;
V klasičnem BIFF8 neznani klic nima lastnega žetona, zato ga HotXLS zapiše tako, kot Excel zapisuje funkcije dodatkov. Formula dobi žeton PtgNameX ($59), katerega vnos XTI kaže na dodatkovni SUPBOOK z obema indeksoma listov nastavljenima na $FFFE, sledijo žetoni argumentov in PtgFuncVar s številko funkcije 255 ter števecem argumentov, ki vključuje režo imena. Podporno telo ExternName je šest ničelnih bajtov, dolžinski bajt in zastavica Unicode, ime funkcije UTF-16, nato dvobajtna formula $1C $17 — PtgErr, ki nosi #REF!. Pisec odklanja imena daljša od 255 znakov, več kot 29 argumentov in cilj BIFF5. Kako HotXLS te dodatkovne vnose SUPBOOK uvršča poleg zunanjih povezav delovnih zvezkov, razlaga pravila razvrščanja SUPBOOK in XTI za zunanje povezave BIFF. XLSX obdrži surovo besedilo funkcije in ODS svojo formulo msoxl:, v vsakem formatu pa se datoteka, ki je shranila =WEBSERVICE(...), ponovno odpre z besedilom nedotaknjenim in se privzeto še vedno ovrednoti v xlfeUnsafeFunctionDenied
Če Vaš cevovod ovrednoti delovne zvezke, ki jih ni napisal sam, pustite AllowUnsafeFormulaCallbacks na False, držite handlerje na izrecnem seznamu dovoljenih in podelite možnosti na klic le tam, kjer je vir formule Vaš. Celoten API za povratne klice, politiko vnosov in ovrednotenje je dokumentiran pri komponenti preglednic HotXLS za Delphi