HotXLS nægter at route 20 farlige formelnavne, blandt andet CALL, REGISTER.ID, WEBSERVICE og DDE, videre til dine Delphi user-function-callbacks, medmindre du selv slår det til. Arbejdsbogs-egenskaben AllowUnsafeFormulaCallbacks står som standard til False, tjekket kører, før noget argument evalueres, og et afvist kald rapporterer xlfeUnsafeFunctionDenied uden at involvere en eneste handler
Scenariet, der gjorde dette nødvendigt, er hverdagsagtigt. En service tager imod uploadede XLS- eller XLSX-filer, genberegner dem server-side og læser et par summer tilbage. Værtsapplikationen registrerede en OnUserFunction-handler for år siden til et par forretningsfunktioner, og på et tidspunkt fik den handler en catch-all-gren, der videresender alt, den ikke genkender, til en plugintabel. Ingen på teamet har nogensinde tastet =WEBSERVICE(...) ind i en celle. Uploaderen gjorde. At holde den formel intakt gennem open, recalc og save er en fil-trohed-funktion. At lade den nå værtskode, der kan åbne sockets eller filer, er en autorisationsbeslutning, og indtil HotXLS adskilte de to, tog biblioteket lydløst den beslutning på dine vegne
Hvorfor blev det at bevare en formel tilladelse til at køre den?
Rodårsagen var én enkelt fallback-sti. HotXLS parser alle Excel-funktionsnavne, den kender, men ikke alle kendte navne har en implementation i beregningsmotoren. Built-ins, der genkendes, men ikke er implementeret, landede tidligere i samme user-defined function-fallback som reelt custom-navne, så CALL og REGISTER.ID delte dispatch-sti med din DISCOUNT eller REGIONRATE. Ukendte navne som WEBSERVICE eller DDE kunne på samme måde matche et enslydende entry i arbejdsbogs-registret, det procesgobrede register eller en event handler. Mekanikken i den fallback er dækket i hvordan HotXLS resolver custom functions gennem OnUserFunction; problemet var, at intet på den sti spurgte, om navnet selv var et, en sund vært nogensinde burde eksekvere
Dispatch-rækkefølgen har betydning for, hvad "ukendt" betyder her. Et kald, motoren ikke kan evaluere nativt, tilbydes skiftevis til leksikale LAMBDA- og LET-bindings, som closure-supporten i HotXLS formelmotor resolver først, derefter til arbejdsbog-lokale funktioner registreret med RegisterUserFunction, derefter til procesgobrede funktioner fra TXLSWorkbook.RegisterGlobalUserFunction og til sidst til OnUserFunction- og OnUserFunctionEx-eventene. Først når alle afviser, bliver en reelt ukendt funktion til #NAME?. Hvert trin efter lambda-opslaget giver kontrollen videre til kode, du selv har skrevet, og det er præcis derfor, sikkerhedstjekket skal sidde foran hele kæden i stedet for inde i en vilkårlig handler
Hvilke funktionsnavne blokerer HotXLS som standard?
XLSFormulaCallbackIsUnsafe i lxCalc.pas holder et fast deny-sæt på 20 navne: 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 og FILE.DELETE. Det er navnene, der i Excel eller dets makrosprog indlæser native kode, når ud på netværket, taler med andre processer eller rører filsystemet. Før sammenligningen trimmer funktionen omgivende whitespace, konverterer navnet til versaler og fjerner ét enkelt _XLFN.- eller _XLWS.-præfiks, så _xlfn.webservice skrevet af en nyere Excel-build fanges ligesom den bare stavning. Listen ligger ved calculator-grænsen frem for i Classic-, XLSX- og ODS-parserne, hvilket holder én AST, én BIFF token stream og én konverteret arbejdsbog til at opføre sig ens
To kanter er værd at kende, før du støtter dig til den. Matchet er eksakt, så en handler, du registrerer som MYWEBSERVICE, er ikke berørt, og omvendt bliver en legitim intern UDF, der tilfældigvis hedder OPEN eller RUN, nu afvist som standard. Deny-sættet er heller ikke en sandbox for dine egne handlers. Eksekverer din catch-all-gren vilkårlige plugin-navne, stopper gaten kun de berygtede farlige og intet andet; den holdbare fix er stadig en handler, der matcher en eksplicit allowlist med SameText og lader Handled stå på False for alt, den ikke ejer
Hvorfor skal gaten køre før argumentevaluering?
En gate, der udløses, efter argumenterne er beregnet, er for sent, fordi argumenterne selv kan kalde din kode. GetValueItemUserFunction tjekker navnet først og afslutter med lxErrorUnsafeFunctionDenied, før den bygger argument-arrayet, før den konsulterer en resolver eller et af registrene, og endda før den lægger mærke til, at der slet ikke er tildelt nogen handler. Den rækkefølge er det, der besejrer det indlejrede tilfælde nedenfor, hvor det ydre kald alligevel ville blive nægtet, men en harmløst udseende indre UDF ellers ville fyre først og efterlade sin sideeffekt
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'); // sideeffekt i værtskode
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, og FAuditLog er stadig tom
Workbook-standard versus pr.-kald TXLSFormulaEvaluationOptions
Arbejdsbogs-flaget er standarden, og pr.-kald-tilvalget har det sidste ord. TXLSWorkbook.AllowUnsafeFormulaCallbacks og TXLSXWorkbook.AllowUnsafeFormulaCallbacks styrer almindelig genberegning, Calculate, den to-argumente EvaluateFormulaAt, evalueringsskabeloner, read-only views og på XLSX hver worker i den parallelle genberegningspool. Ethvert indgangspunkt, der accepterer en eksplicit TXLSFormulaEvaluationOptions-record, tager Options.AllowUnsafeFormulaCallbacks som dommen for det kald og OR'er den ikke med arbejdsbogs-egenskaben. Den asymmetri er bevidst: et betroet internt job kan godkende ét RTD-opslag uden at vende hele arbejdsbogen, og en arbejdsbog, der globalt er optet ind, kan stadig tvinge en sensitiv evaluering tilbage til afvisning
var
Options: TXLSFormulaEvaluationOptions;
Eval: TXLSFormulaEvaluationResult;
begin
// arbejdsbogen forbliver låst, ét betroet kald slipper igennem
Book.AllowUnsafeFormulaCallbacks := False;
Options := XLSDefaultFormulaEvaluationOptions;
Options.AllowUnsafeFormulaCallbacks := True;
Eval := Sheet.EvaluateFormulaAt(4, 2, '=WEBSERVICE(B1)', xlfrsA1, Options);
// arbejdsbogen optet ind, men denne evaluering af uploadet tekst er det ikke
Book.AllowUnsafeFormulaCallbacks := True;
Options := XLSDefaultFormulaEvaluationOptions; // flaget er False igen
Eval := Sheet.EvaluateFormulaAt(4, 2, UploadedFormula, xlfrsA1, Options);
if Eval.Status = xlfeUnsafeFunctionDenied then
LogRejected(Eval.Issue.Message);
end;
Skifter man arbejdsbogs-egenskaben, markeres afhængighedsgrafen også dirty i begge motorer. Uden det trin kunne et cached resultat, beregnet mens callbacks var tilladt, serveres efter, de blev tilbagekaldt, eller et cached xlfeUnsafeFunctionDenied-udfald kunne overleve en opt-in. Den nye status blev tilføjet til TXLSFormulaEvaluationStatus efter xlfeFailed, så den har ordinal 10, og alle eksisterende ordinals beholder deres værdi; samme tail-append-regel gælder options-record-feltet og IXLSWorkbook-getter og -setter, selvom en forbruger bygget mod en ældre udgivelse stadig skal rekompileres
Hvad sker der med ukendt og usikker formeltekst ved gemning?
At bevare en formel og at køre den er nu to adskilte spørgsmål, og entry-politikken besvarer kun det første. FormulaEntryPolicy på begge arbejdsbogsklasser bærer UnknownFunctionMode og UnknownNameMode, begge med standard xlfusmReject, så tildeling af en formel med et ukendt kald gennem den normale Formula-egenskab afvises, før celleværdien, formel-cachen eller afhængighederne ændres. ValidateFormulaEntry rapporterer samme beslutning uden sideeffekter. Betroede stier som filindlæsning, kopiering og formatkonvertering går uden om den bruger-entry-politik, fordi en streng standard aldrig må afvise symboler, der allerede er i en fil, man blot åbner
var
Policy: TXLSFormulaEntryPolicy;
begin
Policy := Book.FormulaEntryPolicy;
Policy.UnknownFunctionMode := xlfusmPreserve; // kompatibilitets-entry
Book.FormulaEntryPolicy := Policy;
Sheet.Cells[3, 1].Formula := '=ACME_RATE(B3)'; // gemt, ikke autoriseret
Book.SaveAs('rates.xls');
end;
I Classic BIFF8 har et ukendt kald ingen token af egen, så HotXLS skriver det, som Excel skriver add-in-funktioner. Formlen får en PtgNameX-token ($59), hvis XTI-entry peger på add-in SUPBOOK med begge arkindekssæt sat til $FFFE, efterfulgt af argument-tokens og en PtgFuncVar med funktionsnummer 255 og et argumentantal, der inkluderer navnepladsen. Den bagvedliggende ExternName-krop er seks nul-bytes, en længdebyte og Unicode-flag, UTF-16-funktionsnavnet, derefter en to-byte formel bestående af $1C $17, en PtgErr med #REF!. Writeren nægter navne længere end 255 tegn, mere end 29 argumenter og BIFF5-målet. Hvordan HotXLS klassificerer disse add-in SUPBOOK-entries ved siden af eksterne arbejdsbogs-links, er forklaret i SUPBOOK- og XTI-klassificeringsreglerne for BIFF eksterne links. XLSX beholder den rå funktionstekst, og ODS beholder sin msoxl:-formel, og i alle formater genåbner en fil, der gemte =WEBSERVICE(...), med teksten intakt og evaluerer stadig til xlfeUnsafeFunctionDenied som standard
Evaluerer din pipeline arbejdsbøger, den ikke selv har forfattet, så lad AllowUnsafeFormulaCallbacks stå på False, hold handlers på en eksplicit allowlist, og giv pr.-kald-tilvalg kun, hvor formelkilden er din egen. Det fulde callback-, entry-policy- og evaluerings-API er dokumenteret hos HotXLS Delphi spreadsheet component