HotXLS, CALL, REGISTER.ID, WEBSERVICE ve DDE dahil 20 tehlikeli formula adını, siz açıkça izin vermedikçe Delphi kullanıcı-fonksiyonu callback'lerinize yönlendirmeyi reddediyor. AllowUnsafeFormulaCallbacks çalışma kitabı özelliği varsayılan olarak False gelir, kontrol hiçbir argüman değerlendirilmeden önce çalışır ve reddedilen bir çağrı tek bir handler'ı dahi çalıştırmadan xlfeUnsafeFunctionDenied raporlar
Bunu gerekli kılan senaryo sıradan. Bir servis yüklenen XLS ya da XLSX dosyalarını kabul ediyor, sunucu tarafında yeniden hesaplıyor ve birkaç toplam geri okuyor. Host uygulama yıllar önce birkaç iş fonksiyonu için OnUserFunction handler'ı kaydetti ve o sırada bir yerde handler, tanımadığı her şeyi bir plugin tablosuna yönlendiren catch-all bir dal büyüttü. Ekibin hiçbir üyesi bir hücreye =WEBSERVICE(...) yazmadı. Dosyayı yükleyen yazdı. O formülü open, recalc ve save boyunca olduğu gibi tutmak dosya-sadakati özelliğidir. Onu soket ya da dosya açabilen host koduna ulaştırmak ise bir yetkilendirme kararıdır ve HotXLS ikisini ayırana kadar kütüphane o kararı sessizçe sizin adınıza veriyordu
Bir formülü korumak, onu çalıştırma iznine nasıl dönüştü?
Kök neden tek bir fallback yoluydu. HotXLS bildiği her Excel fonksiyon adını parse eder ama her bilinen adın hesaplama motorunda bir karşılığı yoktur. Tanınan ama uygulanmamış built-in'ler eskiden gerçekten özel adlarla aynı kullanıcı-tanımlı fonksiyon fallback'ine düşüyordu; CALL ve REGISTER.ID sizin DISCOUNT ya da REGIONRATE'inizle aynı dispatch yolunu paylaşıyordu. WEBSERVICE ya da DDE gibi bilinmeyen adlar da aynı şekilde, çalışma kitabı kayıt defterinde, süreç genelindeki kayıt defterinde ya da bir event handler'da aynı adlı bir girdiyle eşleşebiliyordu. O fallback'in mekaniği HotXLS özel fonksiyonları OnUserFunction üzerinden nasıl çözümlüyor yazısında işleniyor; sorun, o yolda hiçbir şeyin adın kendisinin sağduyulu bir hostun asla çalıştırması gereken bir ad olup olmadığını sormamasıydı
Dispatch sırası, "bilinmeyen"in burada ne anlama geldiğini belirler. Motorun doğal olarak değerlendiremediği bir çağrı sırayla, sözcüksel LAMBDA ve LET bağlamalarına — HotXLS formula motorundaki closure desteğinin önce çözdüğü — sonra RegisterUserFunction ile kaydedilen çalışma kitabı-yerel fonksiyonlara, sonra TXLSWorkbook.RegisterGlobalUserFunction'dan gelen süreç-geneli fonksiyonlara ve son olarak OnUserFunction ile OnUserFunctionEx event'lerine sunulur. Hepsi ret ederse, gerçekten bilinmeyen bir fonksiyon #NAME? olur. Lambda aramasından sonraki her aşama kontrolü sizin yazdığınız koda devreder; güvenlik kontrolünün zincirin herhangi bir handler'ının içinde değil, tamamının önünde oturması gereken yer tam olarak budur
HotXLS varsayılan olarak hangi fonksiyon adlarını engelliyor?
lxCalc.pas içindeki XLSFormulaCallbackIsUnsafe, 20 adlı sabit bir engel kümesi tutar: 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 ve FILE.DELETE. Bunlar, Excel'de ya da makro dilinde yerli kod yükleyen, ağa ulaşan, başka süreçlerle konuşan ya da dosya sistemine dokunan adlardır. Karşılaştırmadan önce fonksiyon çevredeki boşlukları kırpar, adı büyük harfe çevirir ve tek bir _XLFN. ya da _XLWS. önekini soyar; böylece daha yeni bir Excel sürümünün yazdığı _xlfn.webservice, çıplak yazımla aynı biçimde yakalanır. Liste Classic, XLSX ve ODS parser'larında değil hesap makinesi sınırında yaşar; tek bir AST, tek bir BIFF token akışı ve tek bir dönüştürülmüş çalışma kitabının özdeş davranmasını sağlayan da budur
Buna güvenmeden önce bilinmeye değer iki uç var. Eşleşme birebirdir; MYWEBSERVICE olarak kaydettiğiniz bir handler etkilenmez ve tersine, tam adı OPEN ya da RUN tesadüf eden meşru bir şirket-içi UDF artık varsayılan olarak reddedilir. Engel kümesi aynı zamanda kendi handler'larınız için bir sandbox değildir. Catch-all dalınız keyfi plugin adları çalıştırıyorsa kapı, ünlü tehlikelileri durdurur ve başka hiçbir şeyi; kalıcı çözüm hâlâ, açık bir allowlist ile SameText üzerinden eşleşen ve sahibi olmadığı her şey için Handled'ı False bırakan bir handler'dır
Kapı neden argüman değerlendirmesinden önce çalışmak zorunda?
Argümanlar hesaplandıktan sonra tetiklenen bir kapı geç kalmıştır, çünkü argümanların kendisi kodunuzu çağırabilir. GetValueItemUserFunction önce adı kontrol eder ve lxErrorUnsafeFunctionDenied ile çıkar; argüman dizisini kurmadan, bir resolver'a ya da iki kayıt defterinden birine başvurmadan, hatta hiçbir handler atanmamış olduğunu fark etmeden bile önce. Bu sıralama, aşağıdaki iç içe vakayı çökerten şeydir: dış çağrı zaten reddedilecekti ama görünüşte zararsız iç UDF aksi hâlde önce ateşlenir ve yan etkisini geride bırakırdı
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'); // host kodunda yan etki
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, ve FAuditLog hâlâ boş
Çalışma kitabı varsayılanı mı, çağrı başına TXLSFormulaEvaluationOptions mı?
Çalışma kitabı bayrağı varsayılandır, çağrı başına seçenek ise son sözü söyler. TXLSWorkbook.AllowUnsafeFormulaCallbacks ve TXLSXWorkbook.AllowUnsafeFormulaCallbacks, olağan yeniden hesaplamayı, Calculate'i, iki argümanlı EvaluateFormulaAt'ı, değerlendirme şablonlarını, salt-okunur görünümleri ve XLSX'te paralel yeniden hesaplama havuzundaki her worker'ı yönetir. Açık bir TXLSFormulaEvaluationOptions kaydı kabul eden her giriş noktası, o çağrının kararı olarak Options.AllowUnsafeFormulaCallbacks'ı alır ve onu çalışma kitabı özelliğiyle OR'lamaz. Bu asimetri bilinçli: güvenilen bir iç iş, tüm çalışma kitabını devirmeden tek bir RTD aramasına izin verebilir; global olarak izin verilmiş bir çalışma kitabı ise hassas bir değerlendirmeyi yine reddede zorlayabilir
var
Options: TXLSFormulaEvaluationOptions;
Eval: TXLSFormulaEvaluationResult;
begin
// çalışma kitabı kilitli kalır, tek güvenilen çağrı geçer
Book.AllowUnsafeFormulaCallbacks := False;
Options := XLSDefaultFormulaEvaluationOptions;
Options.AllowUnsafeFormulaCallbacks := True;
Eval := Sheet.EvaluateFormulaAt(4, 2, '=WEBSERVICE(B1)', xlfrsA1, Options);
// çalışma kitabı izin veriyor ama yüklenen metnin bu değerlendirmesi vermiyor
Book.AllowUnsafeFormulaCallbacks := True;
Options := XLSDefaultFormulaEvaluationOptions; // bayrak yeniden False
Eval := Sheet.EvaluateFormulaAt(4, 2, UploadedFormula, xlfrsA1, Options);
if Eval.Status = xlfeUnsafeFunctionDenied then
LogRejected(Eval.Issue.Message);
end;
Çalışma kitabı özelliğini değiştirmek her iki motorda da bağımlılık grafiğini dirty işaretler. O adım olmadan, callback'lere izin varken hesaplanmış önbellekteki bir sonuç, izin geri çekildikten sonra servis edilebilirdi ya da önbellekteki bir xlfeUnsafeFunctionDenied sonucu, opt-in'i geçirdi. Yeni durum, TXLSFormulaEvaluationStatus içine xlfeFailed'den sonra eklendi; sıra numarası 10'dur ve mevcut her sıra numarası değerini korur. Aynı sonda-ekleme kuralı options kaydı alanına ve IXLSWorkbook getter ile setter'ına da uygulanır; eski bir sürüme karşı derlenmiş bir tüketicinin yine de yeniden derlemeye ihtiyacı var
Bilinmeyen ve güvensiz formula metnine kaydetme sırasında ne olur?
Bir formülü tutmak ile onu çalıştırmak artık iki ayrı soru ve giriş politikası yalnızca ilkini cevaplıyor. Her iki çalışma kitabı sınıfında da FormulaEntryPolicy, UnknownFunctionMode ve UnknownNameMode taşır; ikisi de varsayılan olarak xlfusmReject gelir, böylece normal Formula özelliği üzerinden bilinmeyen bir çağrı içeren formül atamak, hücre değeri, formula cache ya da bağımlılıklar değişmeden önce reddedilir. ValidateFormulaEntry aynı kararı yan etkisiz raporlar. Dosya yükleme, kopyalama ve format dönüşümü gibi güvenilen yollar o kullanıcı-girişi politikasını aşar; katı bir varsayılan, yalnızca açtığınız bir dosyada zaten bulunan sembolleri asla reddetmemelidir
var
Policy: TXLSFormulaEntryPolicy;
begin
Policy := Book.FormulaEntryPolicy;
Policy.UnknownFunctionMode := xlfusmPreserve; // uyumluluk girişi
Book.FormulaEntryPolicy := Policy;
Sheet.Cells[3, 1].Formula := '=ACME_RATE(B3)'; // saklandı, yetkilendirilmedi
Book.SaveAs('rates.xls');
end;
Classic BIFF8'de bilinmeyen bir çağrının kendi token'ı yoktur; HotXLS onu, Excel'in add-in fonksiyonlarını yazdığı biçimde yazar. Formula, XTI girdisi her iki sheet indeksi de $FFFE'e ayarlı add-in SUPBOOK'una işaret eden bir PtgNameX token'ı ($59) alır; ardından argüman token'ları ve fonksiyon numarası 255, ad yuvasını da kapsayan bir argüman sayısı taşıyan bir PtgFuncVar gelir. Arkasındaki ExternName gövdesi altı sıfır bayt, bir uzunluk baytı ve Unicode bayrağı, UTF-16 fonksiyon adı, sonra $1C $17den oluşan iki baytlık bir formüldür — #REF! taşıyan bir PtgErr. Yazıcı 255 karakterden uzun adları, 29dan fazla argümanı ve BIFF5 hedefini reddeder. HotXLS bu add-in SUPBOOK girdilerini dış çalışma kitabı bağlantılarının yanında nasıl sınıflandırıyor sorusu BIFF dış bağlantıları için SUPBOOK ve XTI sınıflandırma kurallarında açıklanıyor. XLSX ham fonksiyon metnini korur, ODS kendi msoxl: formülünü korur ve her formatta =WEBSERVICE(...) kaydetmiş bir dosya metni sağlam biçimde yeniden açılır ve varsayılan olarak yine xlfeUnsafeFunctionDenied değerlendirilir
Boruhattınız, yazarı olmadığı çalışma kitaplarını değerlendiriyorsa AllowUnsafeFormulaCallbacks'ı False'ta bırakın, handler'ları açık bir allowlist üzerinde tutun ve çağrı başına seçenekleri yalnızca formula kaynağı size aitken verin. Tam callback, giriş-politikası ve değerlendirme API'si HotXLS Delphi spreadsheet component ile birlikte belgelenir