@@ -223,6 +223,34 @@ public void Inspect_ReadsXmpIdentificationFieldsByNamespaceUri() {
223223 Assert . Equal ( "BASIC" , xmp . ElectronicInvoiceConformanceLevel ) ;
224224 }
225225
226+ [ Fact ]
227+ public void Inspect_XmpMetadataRejectsDtdEntityExpansion ( ) {
228+ const string xmp = "<!DOCTYPE xmp [<!ENTITY boom \" expanded\" >]><xmp>&boom;</xmp>" ;
229+
230+ PdfDocumentInfo info = PdfInspector . Inspect ( BuildXmpMetadataPdfWithPayload ( xmp ) ) ;
231+
232+ Assert . True ( info . HasXmpMetadata ) ;
233+ PdfXmpMetadataInfo metadata = Assert . IsType < PdfXmpMetadataInfo > ( info . XmpMetadata ) ;
234+ Assert . Equal ( xmp . Length , metadata . DecodedSizeBytes ) ;
235+ Assert . Contains ( "<!DOCTYPE" , metadata . RawXml , StringComparison . Ordinal ) ;
236+ Assert . False ( metadata . IsWellFormedXml ) ;
237+ Assert . Null ( metadata . Title ) ;
238+ Assert . Empty ( metadata . Subjects ) ;
239+ }
240+
241+ [ Fact ]
242+ public void Inspect_XmpMetadataOverLimitKeepsSizeButDoesNotMaterializeRawXml ( ) {
243+ string xmp = new ( 'x' , PdfReadDocument . MaxXmpMetadataBytes + 1 ) ;
244+
245+ PdfDocumentInfo info = PdfInspector . Inspect ( BuildXmpMetadataPdfWithPayload ( xmp ) ) ;
246+
247+ Assert . True ( info . HasXmpMetadata ) ;
248+ PdfXmpMetadataInfo metadata = Assert . IsType < PdfXmpMetadataInfo > ( info . XmpMetadata ) ;
249+ Assert . Equal ( PdfReadDocument . MaxXmpMetadataBytes + 1 , metadata . DecodedSizeBytes ) ;
250+ Assert . Null ( metadata . RawXml ) ;
251+ Assert . False ( metadata . IsWellFormedXml ) ;
252+ }
253+
226254 [ Fact ]
227255 public void Preflight_AllowsSimpleCatalogUriPdfReadAndRewrite ( ) {
228256 PdfDocumentPreflight report = PdfInspector . Preflight ( BuildCatalogUriPdf ( ) ) ;
@@ -363,5 +391,36 @@ public void Inspect_DecodesFilteredOutputIntentProfileBeforeReadingIccHeader() {
363391 Assert . True ( outputIntent . DestinationOutputProfileHasIccSignature ) ;
364392 }
365393
394+ private static byte [ ] BuildXmpMetadataPdfWithPayload ( string xmp ) {
395+ string pdf = string . Join ( "\n " , new [ ] {
396+ "%PDF-1.4" ,
397+ "1 0 obj" ,
398+ "<< /Type /Catalog /Pages 2 0 R /Metadata 5 0 R >>" ,
399+ "endobj" ,
400+ "2 0 obj" ,
401+ "<< /Type /Pages /Count 1 /Kids [3 0 R] >>" ,
402+ "endobj" ,
403+ "3 0 obj" ,
404+ "<< /Type /Page /Parent 2 0 R /MediaBox [0 0 200 200] /Contents 4 0 R >>" ,
405+ "endobj" ,
406+ "4 0 obj" ,
407+ "<< /Length 0 >>" ,
408+ "stream" ,
409+ "" ,
410+ "endstream" ,
411+ "endobj" ,
412+ "5 0 obj" ,
413+ "<< /Type /Metadata /Subtype /XML /Length " + System . Text . Encoding . UTF8 . GetByteCount ( xmp ) . ToString ( System . Globalization . CultureInfo . InvariantCulture ) + " >>" ,
414+ "stream" ,
415+ xmp ,
416+ "endstream" ,
417+ "endobj" ,
418+ "trailer" ,
419+ "<< /Root 1 0 R /Size 6 >>" ,
420+ "%%EOF"
421+ } ) ;
422+
423+ return System . Text . Encoding . UTF8 . GetBytes ( pdf ) ;
424+ }
366425
367426}
0 commit comments