HotXLS vägrar skicka 20 farliga formelnamn, bland dem CALL, REGISTER.ID, WEBSERVICE och DDE, till dina Delphi-återanrop för användarfunktioner om du inte uttryckligen aktiverar det. Egenskapen AllowUnsafeFormulaCallbacks på arbetsboken har False som standard, kontrollen körs innan något argument evalueras, och ett avvisat anrop rapporterar xlfeUnsafeFunctionDenied utan att anropa en enda hanterare
Scenariot som gjorde detta nödvändigt är vardagligt. En tjänst tar emot uppladdade XLS- eller XLSX-filer, räknar om dem på servern och läser tillbaka några summor. Värdapplikationen registrerade en OnUserFunction-hanterare för några år sedan för ett par affärsfunktioner, och någonstans på vägen växte den hanteraren fram en catch-all-gren som vidarebefordrar allt den inte känner igen till en plugintabell. Ingen i teamet skrev någonsin =WEBSERVICE(...) i en cell. Uppladdaren gjorde det. Att bevara den formeln orörd genom öppning, omräkning och sparning är filfidelitet, en finess. Att låta den nå värdkod som kan öppna uttag eller filer är däremot ett auktorisationsbeslut, och förrän HotXLS skilde åt de två tog biblioteket det beslutet tyst åt dig
Varför blev att bevara en formel detsamma som tillstånd att köra den?
Rotorsaken var en enda reservväg. HotXLS parsar varje Excel-funktionsnamn den känner till, men inte alla kända namn har en implementering i beräkningsmotorn. Inbyggda funktioner som känns igen men saknar implementering hamnade förr i samma reservväg för användardefinierade funktioner som genuint anpassade namn, så CALL och REGISTER.ID delade en dispatchväg med din DISCOUNT eller REGIONRATE. Okända namn som WEBSERVICE eller DDE kunde på samma sätt matcha en post med samma namn i arbetsbokens register, processens register eller en händelsehanterare. Mekaniken i den reservvägen tas upp i hur HotXLS löser anpassade funktioner via OnUserFunction; problemet var att inget på den vägen frågade om namnet själv var ett som en sund värd överhuvudtaget borde köra
Dispatchordningen spelar roll för vad "okänd" betyder här. Ett anrop motorn inte kan evaluera nativt erbjuds i tur och ordning till lexikala LAMBDA- och LET-bindningar, som stödet för stängningar i HotXLS formelmotor löser först, sedan till arbetsbokslokala funktioner registrerade med RegisterUserFunction, därefter till processomfattande funktioner från TXLSWorkbook.RegisterGlobalUserFunction, och slutligen till händelserna OnUserFunction och OnUserFunctionEx. Först när alla tackar nej blir en verkligt okänd funktion #NAME?. Varje steg efter uppslaget av lambda överlämnar kontrollen till kod du skrivit, vilket är precis varför säkerhetskontrollen måste sitta framför hela kedjan i stället för inuti någon enskild hanterare
Vilka funktionsnamn blockerar HotXLS som standard?
XLSFormulaCallbackIsUnsafe i lxCalc.pas håller en fast förbjuden uppsättning på 20 namn: 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 och FILE.DELETE. Det är namnen som, i Excel eller dess makrospråk, laddar native-kod, når nätverket, pratar med andra processer eller rör vid filsystemet. Före jämförelsen trimmar funktionen omgivande blanktecken, versaler namnet och stryker ett enskilt prefix _XLFN. eller _XLWS., så _xlfn.webservice skrivet av en nyare Excel-version fångas precis som den nakna stavningen. Listan bor vid kalkylatorgränsen snarare än i parserna för Classic, XLSX och ODS, så att en AST, en BIFF-tokenström och en konverterad arbetsbok beter sig identiskt
Två kanter är värda att känna till innan du litar på den. Matchen är exakt, så en hanterare du registrerar som MYWEBSERVICE påverkas inte, och omvänt nekas en legitim intern UDF som råkar heta OPEN eller RUN nu som standard. Den förbjudna uppsättningen är heller inte en sandlåda för dina egna hanterare. Om din catch-all-gren kör godtyckliga pluginnamn stoppar grinden de berömda farliga och inget annat; den hållbara fixen är fortfarande en hanterare som matchar en explicit tillåten lista med SameText och lämnar Handled på False för allt den inte äger
Varför måste grinden köras innan argumenten evalueras?
En grind som slår till efter att argumenten räknats ut kommer för sent, för argumenten själva kan anropa din kod. GetValueItemUserFunction kontrollerar namnet först och avslutar med lxErrorUnsafeFunctionDenied innan den bygger argumentarrayen, innan den frågar en resolver eller något av registren, och till och med innan den noterar att ingen hanterare alls är kopplad. Den ordningen är vad som besegrar det nästlade fallet nedan, där det yttre anropet ändå skulle vägras, men en inre UDF som ser oskyldig ut annars hade avfyrats först och lämnat sin bieffekt kvar
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'); // bieffekt i värdkod
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, och FAuditLog är fortfarande tom
Arbetsbokens standard kontra per-anrop TXLSFormulaEvaluationOptions
Arbetsboksflaggan är standarden och per-anrop-optionen har sista ordet. TXLSWorkbook.AllowUnsafeFormulaCallbacks och TXLSXWorkbook.AllowUnsafeFormulaCallbacks styr vanlig omräkning, Calculate, tvåargumentversionen av EvaluateFormulaAt, evalueringsmallar, skrivskyddade vyer och, på XLSX, varje arbetare i den parallella omräkningspoolen. Varje ingångspunkt som tar emot en explicit TXLSFormulaEvaluationOptions-post använder Options.AllowUnsafeFormulaCallbacks som dom för det anropet och kombinerar den inte med arbetsboksegenskapen. Den asymmetrin är medveten: ett betrott internt jobb kan auktorisera ett enda RTD-uppslag utan att vända hela arbetsboken, och en arbetsbok som är globalt aktiverad kan fortfarande tvinga en känslig evaluering tillbaka till nekande
var
Options: TXLSFormulaEvaluationOptions;
Eval: TXLSFormulaEvaluationResult;
begin
// arbetsboken förblir låst, ett betrott anrop släpps igenom
Book.AllowUnsafeFormulaCallbacks := False;
Options := XLSDefaultFormulaEvaluationOptions;
Options.AllowUnsafeFormulaCallbacks := True;
Eval := Sheet.EvaluateFormulaAt(4, 2, '=WEBSERVICE(B1)', xlfrsA1, Options);
// arbetsboken aktiverad, men denna evaluering av uppladdad text är det inte
Book.AllowUnsafeFormulaCallbacks := True;
Options := XLSDefaultFormulaEvaluationOptions; // flaggan är False igen
Eval := Sheet.EvaluateFormulaAt(4, 2, UploadedFormula, xlfrsA1, Options);
if Eval.Status = xlfeUnsafeFunctionDenied then
LogRejected(Eval.Issue.Message);
end;
Att växla arbetsboksegenskapen märker också beroendegrafen smutsig i båda motorerna. Utan det steget skulle ett cachat resultat beräknat medan återanrop var tillåtna kunna serveras efter att de återkallats, eller så kunde ett cachat utfall xlfeUnsafeFunctionDenied överleva en aktivering. Den nya statusen lades till TXLSFormulaEvaluationStatus efter xlfeFailed, så den har ordinal 10 och varje befintlig ordinal behåller sitt värde; samma regel om tillägg på svansen gäller fältet i optionsposten och gettern och settern i IXLSWorkbook, även om en konsument byggd mot en äldre utgåva fortfarande behöver kompileras om
Vad händer med okänd och osäker formeltext vid sparning?
Att behålla en formel och att köra den är nu två skilda frågor, och ingångspolicyn svarar bara på den första. FormulaEntryPolicy på vardera arbetsboksklassen bär UnknownFunctionMode och UnknownNameMode, båda med xlfusmReject som standard, så att tilldela en formel med ett okänt anrop via den vanliga egenskapen Formula avvisas innan cellvärdet, formelcachen eller beroendena ändras. ValidateFormulaEntry rapporterar samma beslut utan biverkningar. Betrodda vägar som filläsning, kopiering och formatkonvertering kringgår den användaringångspolicyn, för en strikt standard får aldrig avvisa symboler som redan finns i en fil du bara öppnar
var
Policy: TXLSFormulaEntryPolicy;
begin
Policy := Book.FormulaEntryPolicy;
Policy.UnknownFunctionMode := xlfusmPreserve; // kompatibilitetsingång
Book.FormulaEntryPolicy := Policy;
Sheet.Cells[3, 1].Formula := '=ACME_RATE(B3)'; // lagrad, inte auktoriserad
Book.SaveAs('rates.xls');
end;
I Classic BIFF8 har ett okänt anrop ingen egen token, så HotXLS skriver det som Excel skriver tilläggsfunktioner. Formeln får en PtgNameX-token ($59) vars XTI-post pekar på tilläggets SUPBOOK med båda bladindexen satta till $FFFE, följt av argumenttokenen och en PtgFuncVar som bär funktionsnummer 255 och ett argumentantal som inkluderar namnplatsen. Den underliggande ExternName-kroppen är sex nollbyte, en längdbyte och Unicodeflagga, UTF-16-namnet, och därefter en tvåbytesformel av $1C $17, en PtgErr som håller #REF!. Skrivaren vägrar namn längre än 255 tecken, fler än 29 argument och BIFF5-målet. Hur HotXLS klassificerar dessa tilläggs-SUPBOOK-poster bredvid externa arbetsbokslänkar förklaras i SUPBOOK- och XTI-klassificeringsreglerna för BIFF externa länkar. XLSX behåller den råa funktionstexten och ODS behåller sin msoxl:-formel, och i varje format öppnas en fil som sparade =WEBSERVICE(...) igen med texten intakt och evaluerar fortfarande till xlfeUnsafeFunctionDenied som standard
Evaluerar din pipeline arbetsböcker den inte själv författat, låt AllowUnsafeFormulaCallbacks stå på False, håll hanterare på en explicit tillåten lista, och bevilja per-anrop-optioner bara där formelkällan är din egen. Hela återanrops-, ingångspolicy- och evaluerings-API:et är dokumenterat hos HotXLS Delphi-kalkylbladskomponenten