Úvod

Kolega vám pošle dvě revize smlouvy a požádá vás, abyste je porovnali. Obě soubory vložíte do své služby pro porovnání, výsledek se vrátí a vše vypadá normálně. To, co jste neviděli, je, že jeden z dokumentů obsahoval odkazovaný obrázek směřující na URL a váš server kontaktoval tento hostitel ve chvíli, kdy byl soubor otevřen. Žádná část výstupu vám neřekne, že se to stalo.

Nejedná se o chybu – je to to, co znamená načíst dokument věrně. Soubor OOXML může odkazovat na obrázek, který žije na webovém serveru místo toho, aby byl uvnitř balíčku, a jak Word, tak i jakákoli knihovna, která dokument načítá, tento odkaz správně rozpozná. GroupDocs.Comparison pro .NET poskytuje dvě vlastnosti na LoadOptions, které vám umožní rozhodnout, zda to udělá: SkipExternalResources a WhitelistedResources.

Mezi nimi existují tři konfigurace a tento článek porovnává všechny tři – permissivní výchozí nastavení, blokování všeho a blokování všeho kromě pojmenovaných odkazů. Na konci budete vědět, kterou zvolit pro daný zdroj dokumentu, a dvě chyby, které způsobují, že tato nastavení vypadají, jako by nefungovala.

💡 Plný funkční příklad: block-external-resources-on-document-load-dotnet – spustitelný konzolový projekt, který sám obsluhuje odkazované obrázky a zaznamenává každý požadavek, takže můžete sledovat, jak se jednotlivá nastavení projeví.

Kde se skrývají externí odkazy

Než si vyberete nastavení, stojí za to vědět, co vlastně volíte. Soubor .docx nese externí odkazy na dvou odlišných místech a jsou snadno přehlédnutelné, protože žádné z nich není viditelné v textu dokumentu.

První je vztah v word/_rels/document.xml.rels obsahující TargetMode="External" a absolutní URL. Obrázek se v těle objeví jako kresba, která odkazuje na vztah podle ID, takže samotná URL se nikdy neobjeví v blízkosti obsahu, který ovlivňuje.

Druhá je kód pole INCLUDEPICTURE v těle dokumentu, který drží svou URL uvnitř instrukce pole. Word ji rozpozná při vykreslování stránky; knihovna pro porovnání ji rozpozná při načítání dokumentu.

Oba mechanismy respektují dvě výše diskutované možnosti načítání, což je důležité, protože dokument může použít jeden nebo oba. Odkaz, který jste zaznamenali v souboru vztahů, není důkazem, že ve kódu pole neexistuje druhý.

Přístup 1: Výchozí - Odkazy rozpoznány

SkipExternalResources má výchozí hodnotu false, takže dokument načtený bez konfigurace má své vzdálené odkazy rozpoznány:

LoadOptions loadOptions = new LoadOptions
{
    SkipExternalResources = false
};

using (Comparer comparer = new Comparer(sourcePath, loadOptions))
{
    comparer.Add(targetPath, loadOptions);
    comparer.Compare(outputPath);
}

Toto poskytuje nejvyšší věrnost: porovnávané dokumenty obsahují vše, na co odkazují, přesně tak, jak by je Word vykreslil. Pro dokumenty, které vaše vlastní aplikace nebo šablony vytvořily, kde každá URL odkazuje na infrastrukturu, kterou provozujete, je to správná volba – a chybějící odkazovaný obrázek by mohl porovnání aktivně zavádět.

Cena je, že každý odkaz je kontaktován, ať už jej tam umístil kdokoli. Existuje také časová cena, která nemá nic společného s důvěrou: odkazová URL, která již nefunguje, způsobí, že načítání čeká na celý pokus o připojení u každého jednotlivého porovnání.

Přístup 2: Blokovat každý externí zdroj

Jedna vlastnost vypne rozpoznávání vzdálených odkazů pro daný dokument:

LoadOptions loadOptions = new LoadOptions
{
    SkipExternalResources = true
};

using (Comparer comparer = new Comparer(sourcePath, loadOptions))
{
    comparer.Add(targetPath, loadOptions);
    comparer.Compare(outputPath);
}

Žádný požadavek není odeslán. Odkazované obrázky chybí ve výsledku a – to je část, kterou je třeba jasně uvést – nic jiného se nezmění. Nastavení řídí, co se načte, ne jak se hledají rozdíly, takže textové a strukturální změny mezi dvěma dokumenty jsou detekovány přesně jako předtím. Jediná věc, kterou ztratíte, je schopnost detekovat změnu uvnitř odkazovaného obrázku, který nikdy nebyl načten.

Toto je konfigurace, kterou byste měli považovat za výchozí pro dokumenty, které jste nevytvořili: nahrané soubory uživatelem ve webové aplikaci, soubory přijaté e-mailem, cokoliv porovnávaného na build agentu, kde odchozí požadavek není běžně zamýšlen. Je to vše nebo nic, ačkoliv – odkazovaný obrázek, který jste skutečně chtěli, je blokován spolu se zbytkem a výsledek jej jednoduše postrádá, aniž by to oznámil.

Přístup 3: Blokovat vše kromě pojmenovaných odkazů

Třetí konfigurace je ta, která odměňuje pečlivé čtení. WhitelistedResources přijímá List<string> a je konzultována pouze tehdy, když je SkipExternalResources nastaveno na true:

LoadOptions loadOptions = new LoadOptions
{
    SkipExternalResources = true,
    WhitelistedResources = new List<string> { "includepicture-field.png" }
};

using (Comparer comparer = new Comparer(sourcePath, loadOptions))
{
    comparer.Add(targetPath, loadOptions);
    comparer.Compare(outputPath);
}

Položky jsou fragmenty URL, ne názvy souborů. Každý je porovnán s odkazovou URL a shoda kdekoli v ní povolí tento zdroj. To je to, co dělá whitelist přenosným: "includepicture-field.png" povolí obrázek bez ohledu na schéma, hostitele a cestu, které mu předcházejí, takže stejný seznam funguje ve vývoji i v produkci bez úprav.

Stejná vlastnost může fungovat i opačně. Krátký nebo obecný fragment – logo.png, nebo ještě horší, .png – může odpovídat odkazům, které jste nikdy nechtěli povolit. Vyberte fragment dostatečně specifický, aby identifikoval ten jediný zdroj, který máte na mysli.

Ve vzorovém odkazu tato konfigurace načte whitelistovaný obrázek a druhý odkazovaný obrázek, který žádná položka nepokrývá, zůstane blokován. Protokol požadavků ukazuje tři požadavky, kde permissivní výchozí nastavení vytvořilo pět, a uvádí pouze whitelistovaný soubor.

Kterou konfiguraci byste měli použít?

Přizpůsobte nastavení tomu, odkud dokument pochází. Dokumenty, které vaše vlastní aplikace nebo šablony vytvořily, mohou zachovat výchozí nastavení, protože každá URL odkazuje na infrastrukturu, kterou již provozujete. Cokoliv přicházejícího zvenčí – nahrané soubory uživatelem, e‑mailové přílohy, soubory třetích stran – vyžaduje SkipExternalResources = true. Přidejte úzký fragment WhitelistedResources pouze tehdy, když jeden důvěryhodný odkaz skutečně musí být rozpoznán.

Porovnání tří

Zájem Výchozí Blokovat vše Blokovat + whitelist
Počet nastavených vlastností 0 1 2
Odchozí požadavky všechny odkazy žádné jen whitelistované
Kontrola na úrovni odkazu ne ne ano
Mrtvé URL zvyšují dobu načítání ano ne jen whitelistované
Nejvhodnější pro dokumenty, které jste vytvořili dokumenty z jakéhokoli jiného zdroje důvěryhodné šablony mezi nedůvěryhodným obsahem

Rozhodnutí vychází z provenance dokumentu, nikoli z výkonu. Dokumenty generované vašimi systémy mohou zachovat výchozí nastavení. Dokumenty z vnějšího prostředí vyžadují blokování. Whitelistujte jen v okamžiku, kdy jeden konkrétní odkaz skutečně musí být rozpoznán – například firemní šablona, která si načítá hlavičkový obrázek z interní URL, mezi zprávami, jejichž autoři vkládali obrázky odkudkoliv chtěli.

Dvě chyby

Obě tyto chyby způsobují stejný symptom: nastavíte možnost a zdá se, že nic nedělá.

Whitelist bez přepínače. WhitelistedResources je konzultována pouze tehdy, když je SkipExternalResources nastaveno na true. Pouze nastavený whitelist nedělá nic – neexistuje blokování, ke kterému by mohl udělat výjimku. Pokud se vám zdá, že whitelist je ignorován, zkontrolujte nejprve tuto věc.

Možnosti pouze na zdrojovém dokumentu. Toto je subtilnější. Možnosti načítání popisují, jak je jeden dokument načten. Konstruktor Comparer přijímá možnosti pro zdroj; každé volání Add() přijímá možnosti pro daný cíl:

using (Comparer comparer = new Comparer(sourcePath, loadOptions))
{
    comparer.Add(targetPath, loadOptions);
    comparer.Compare(outputPath);
}

Předáte je do konstruktoru a zapomenete na volání Add(), a zdroj je chráněn, zatímco každý cíl stále načítá své odkazy. Porovnání uspěje, výsledek vypadá věrohodně a polovina vašich dokumentů stále kontaktuje síť. Když zdroj a cíl potřebují odlišné zacházení, předávejte samostatné instance LoadOptions – to je přesně důvod, proč API přijímá možnosti na úrovni dokumentu.

Ověření, že to skutečně fungovalo

Blokovaný zdroj téměř nezanechává žádnou stopu. Výstupní dokument postrádá obrázek, což vypadá podobně jako dokument, který nikdy žádný obrázek neměl. Čtení výsledného souboru je proto špatný způsob, jak potvrdit, že nastavení vstoupilo v platnost.

Sledujte místo, kde se obrázky obsluhují. Vzorový odkaz záměrně používá tento přístup: spustí malý HTTP listener na volném loopback portu, zapíše demonstrační dokumenty odkazující na tento port a zaznamenává každý přijatý požadavek, přičemž tiskne počet požadavků na každé porovnání. Pět požadavků, pak nula, pak tři. Síťový záznam proti vašim skutečným zdrojům dokumentů vám poskytne stejnou jistotu.

Závěr

Tři konfigurace, jedno rozhodovací pravidlo: nechte dokumenty, které jste vytvořili, zachovat výchozí nastavení, nastavte SkipExternalResources = true pro vše ostatní a whitelistujte úzký fragment URL jen tam, kde konkrétní důvěryhodný odkaz stále potřebuje být rozpoznán.

Pak zkontrolujte dvě věci, které tiše ruší vaši práci – whitelist bez SkipExternalResources = true a možnosti předané konstruktoru Comparer, ale ne každému volání Add() – a ověřujte z obslužné strany místo výstupního souboru.

Další zdroje