Ένα βιβλίο εργασίας Excel μπορεί να κουβαλά εικόνες EMF και WMF, και ο συμβατικός τρόπος να ζωγραφίσει κανείς μία είναι να δώσει τη ροή bytes στον αναπαραγωγό metafile του λειτουργικού συστήματος. Είναι μια απόφαση που αξίζει να κοιταχθεί ευθεία: ένα metafile είναι μια σειριοποιημένη ροή εντολών για ένα API γραφικών, και η αναπαραγωγή του σημαίνει να αφήσετε ένα αρχείο που έφτασε μέσω email να οδηγήσει τον οδηγό γραφικών. Το HotXLS παίρνει την άλλη διαδρομή. Η XLSDecodeVectorScene αναλύει το metafile η ίδια, επαληθεύει την κεφαλίδα, κάθε μέγεθος εγγραφής, το δηλωμένο σύνολο εγγραφών και την ακριβή θέση της εγγραφής τέλους αρχείου, απορρίπτει ρητά τις εγγραφές escape, και επιστρέφει μια TXLSVectorScene από πρωτογενείς εντολές ζωγραφικής που τα backends Canvas και SVG αναπαράγουν μέσω του δικού τους κώδικα. Καμία αναπαραγωγή οδηγού δεν εμπλέκεται σε κανένα σημείο
Η ανταλλαγή είναι κάλυψη έναντι περιορισμού. Μια whitelist εντολών προσανατολισμένη σε ορθογώνια δεν θα αναπαράγει κάθε metafile που μπορεί να δημιουργήσει ένας σχεδιαστής, οπότε η σκηνή αναφέρει πόσες εγγραφές ζωγραφικής δεν μπόρεσε να αναπαραστήσει και ο καλών αποφασίζει τι θα κάνει με αυτό. Για μια διεργασία διακομιστή που αποδίδει έγγραφα που δεν δημιούργησε, εκείνη η ανταλλαγή είναι ο σωστός προσανατολισμός
Γιατί η αναπαραγωγή metafile δεν ταιριάζει σε μη αξιόπιστη είσοδο;
Επειδή η μορφή δεν είναι εικόνα, είναι πρόγραμμα. Μια ροή εγγραφών EMF χειρίζεται μια στοίβα καταστάσεων device context, δεσμεύει και επιλέγει objects από πίνακα handles, και μπορεί να κουβαλά εγγραφές escape των οποίων το payload περνά σε οδηγό συσκευής. Η αναπαραγωγή της ασκεί διαδρομές στη στοίβα γραφικών της πλατφόρμας που γράφτηκαν με την υπόθεση ότι το metafile προήλθε από συνεργαζόμενη εφαρμογή στο ίδιο μηχάνημα. Όταν η είσοδος είναι ένα συνημμένο λογιστικού φύλλου, εκείνη η υπόθεση έχει χαθεί, και καμία ποσότητα προσοχής μέσα στη βιβλιοθήκη λογιστικού φύλλου δεν βοηθά επειδή η βιβλιοθήκη δεν είναι το στοιχείο που αναλύει
Αυτός είναι ο ίδιος συλλογισμός που διέπει το στρώμα container. Ένα βιβλίο εργασίας είναι ένα αρχείο ZIP, και το HotXLS επαληθεύει τον κεντρικό του κατάλογο αντί να εμπιστεύεται δηλωμένα offsets, όπως περιγράφεται στο άρθρο για την επαλήθευση ZIP end-of-central-directory. Τα payloads metafile είναι το επόμενο στρώμα του ίδιου προβλήματος
Τι ελέγχει ο αποκωδικοποιητής πριν ζωγραφίσει οτιδήποτε
Η επαλήθευση είναι δομική και συμβαίνει εξαρχής, επειδή ένας parser που αρχίζει να ζωγραφίζει και επαληθεύει καθώς προχωρά έχει ήδη δράσει πάνω σε δεδομένα που δεν επαλήθευσε. Η κεφαλίδα πρέπει να ταιριάζει αυστηρά και όχι πειστικά. Κάθε εγγραφή πρέπει να δηλώνει μέγεθος που χωρά μέσα στον υπόλοιπο buffer και είναι αρκετά μεγάλο για τα δικά της σταθερά πεδία. Ο αριθμός εγγραφών που δηλώνει η κεφαλίδα πρέπει να ταιριάζει με τις εγγραφές που υπάρχουν πράγματι. Η εγγραφή τέλους αρχείου πρέπει να κάθεται ακριβώς εκεί που τελειώνει η ροή, όχι απλώς κάπου κοντά της, που κλείνει το κόλπο των τελικών σκουπιδιών που κρύβει ένα δεύτερο payload πίσω από μια έγκυρη εικόνα
Πέρα από τη δομή, ο αποκωδικοποιητής είναι fail-closed στη σημασιολογία. Οι εγγραφές escape απορρίπτονται, δεν παραλείπονται. Μια εγγραφή αλλαγής κατάστασης που ο αποκωδικοποιητής δεν μοντελοποιεί προκαλεί αποτυχία της αποκωδικοποίησης αντί να αγνοηθεί, επειδή η αγνόηση μιας αλλαγής κατάστασης σημαίνει ότι κάθε μεταγενέστερη εντολή ζωγραφικής εκτελείται σε κατάσταση που το αρχείο δεν ζήτησε, και το αποτέλεσμα είναι μια εικόνα λάθος με τρόπο που κανείς δεν μπορεί να προβλέψει. Οι εγγραφές ζωγραφικής έξω από το υποστηριζόμενο σύνολο εντολών είναι διαφορετική υπόθεση: εκείνες μετριούνται και παραλείπονται, επειδή ένα απόν σχήμα είναι ένα ορατό, αναφερόμενο κενό και όχι σιωπηλή καταστροφή
Τα budgets είναι μέρος της σύμβασης μορφής
Οι διανυσματικές μορφές έχουν τη δική τους εκδοχή της βόμβας αποσυμπίεσης. Λίγα kilobytes εγγραφών μπορούν να δηλώσουν polylines με εκατοντάδες εκατομμύρια σημεία, ή μια εικόνα της οποίας οι δηλωμένες διαστάσεις πολλαπλασιάζονται σε terabytes. Τα όρια πρέπει επομένως να είναι ρητές σταθερές και όχι ό,τι τυχαίνει να επιβιώσει το μηχάνημα
// Από το lxVectorScene: ο προϋπολογισμός αποκωδικοποίησης, δηλωμένος
// και όχι υπονοούμενος
XL_VECTOR_MAX_RECORDS = 1000000;
XL_VECTOR_MAX_HANDLES = 4096;
XL_VECTOR_MAX_DC_DEPTH = 32;
XL_VECTOR_MAX_COMMANDS = 100000;
XL_VECTOR_MAX_POINTS_PER_RECORD = 100000;
XL_VECTOR_MAX_TOTAL_POINTS = 2000000;
XL_VECTOR_MAX_TEXT_CHARS = 4096;
XL_VECTOR_MAX_TOTAL_TEXT_CHARS = 1000000;
XL_VECTOR_MAX_IMAGE_SIDE = 8192;
XL_VECTOR_MAX_IMAGE_PIXELS = 32 * 1024 * 1024;
XL_VECTOR_MAX_IMAGE_BYTES = 64 * 1024 * 1024;
XL_VECTOR_MAX_COORD = 1000000000;
Δύο από αυτά αξίζουν σημείωση. Το όριο βάθους device context 32 υπάρχει επειδή οι εγγραφές SaveDC και RestoreDC φωλιάζουν, και μια ανισόρροπη ροή μπορεί να σπρώχνει για πάντα· το 32 είναι γενναιόδωρο για πραγματικά metafiles και φθηνό να επιβληθεί. Το όριο συντεταγμένων υπάρχει επειδή οι συντεταγμένες τροφοδοτούν έναν μετασχηματισμό, και μια τιμή κοντά στα όρια του εύρους ακεραίων παράγει μετασχηματισμένο αποτέλεσμα που είναι είτε άπειρο είτε τυλίγει, μετά από το οποίο κάθε υπολογισμός bounding box κατάντη είναι ανοησία. Ο περιορισμός συντεταγμένων τη στιγμή της ανάλυσης είναι πολύ ευκολότερος στη συλλογιστική από την άμυνα σε κάθε καταναλωτή της γεωμετρίας
Χρήση της σκηνής
Ο αποκωδικοποιητής παραδίδει ένα object που ανήκει σε εσάς, έναν αριθμό εντολών, ένα ονομαστικό μέγεθος, και έναν αριθμό εγγραφών ζωγραφικής που διάλεξε να μην αναπαραστήσει
uses
lxVectorScene;
var
Scene: TXLSVectorScene;
Error: WideString;
I: Integer;
begin
// Τα Data κρατούν το γυμνό payload εικόνας παρμένο από το βιβλίο εργασίας
if not XLSDecodeVectorScene(Data, xlsvfEmf, Scene, Error) then
begin
// Απορρίφθηκε: κεφαλίδα, όρια, σύνολα, θέση EOF ή ένα budget
LogReject('metafile rejected: ' + Error);
Exit;
end;
try
if Scene.SkippedDrawRecords > 0 then
LogWarning(Format('%d drawing records outside the safe subset',
[Scene.SkippedDrawRecords]));
for I := 0 to Scene.Count - 1 do
case Scene.Commands[I].Kind of
xlsvcRectangle: DrawRect(Scene.Commands[I]);
xlsvcEllipse: DrawEllipse(Scene.Commands[I]);
xlsvcPolyline,
xlsvcPolygon,
xlsvcBezier: DrawPath(Scene.Commands[I]);
xlsvcText: DrawText(Scene.Commands[I]);
xlsvcImage: DrawImage(Scene.Commands[I]);
end;
finally
Scene.Free;
end;
end;
Η εγγραφή εντολής κουβαλά ό,τι χρειάζεται ένα backend και τίποτα που απαιτεί συσκευή: παρουσία πένας, χρώμα, πλάτος και στυλ· παρουσία πινέλου και χρώμα· τη γεωμετρία· και για κείμενο τη συμβολοσειρά, όνομα γραμματοσειράς, μέγεθος, στυλ και στοίχιση. Αυτό κάνει την ίδια σκηνή χρησιμοποιήσιμη και από τον renderer canvas οθόνης και από τον writer SVG, και είναι ο λόγος που η διανυσματική διαδρομή δεν αποκλίνει ανάμεσα σε προεπισκόπηση και εξαγωγή. Η απόδοση περιεχομένου φύλλου σε οθόνη γενικά καλύπτεται στο άρθρο για την απόδοση προσαρμοσμένου πλέγματος VCL
Η απόρριψη μιας εικόνας δεν βλάπτει το βιβλίο εργασίας
Μια σημαντική ιδιότητα αυτού του σχεδιασμού είναι ότι μια απορριφθείσα αποκωδικοποίηση επηρεάζει μόνο την απόδοση. Το αρχικό payload παραμένει στο μοντέλο, οπότε ένα βιβλίο εργασίας που ανοίγεται και αποθηκεύεται ξανά κουβαλά τις εικόνες metafile του έξω byte προς byte, αν ο ασφαλής αποκωδικοποιητής μπορούσε να τις ζωγραφίσει ή όχι. Η υπάρχουσα περιορισμένη ραστεροποιημένη διαδρομή παραμένει επίσης διαθέσιμη ως εναλλακτική. Με άλλα λόγια, ο αυστηρός parser φράσσει τι εκτελείται, όχι τι διατηρείται, που είναι η διάκριση που αφήνει μια αλλαγή εμπνευσμένη από ασφάλεια να κυκλοφορήσει χωρίς να γίνει αλλαγή απώλειας δεδομένων
Ο χειρισμός αντικειμένων σχεδίου γενικά, συμπεριλαμβανομένων των μερών του μοντέλου αντικειμένων που επιβιώνουν ανέγγιχτα από round-trips, καλύπτεται στο άρθρο για διαγράμματα, εικόνες και σχέδια
Πού αφήνει αυτό μια ανάπτυξη διακομιστή
Αν αποδίδετε βιβλία εργασίας που ανεβάζουν χρήστες σε μια υπηρεσία, η πρακτική θέση είναι πλέον υπερασπίσιμη: οι εικόνες metafile αναλύονται από κώδικα που μπορείτε να ελέγξετε, οριοθετούνται από σταθερές που μπορείτε να διαβάσετε, και δεν παραδίδονται ποτέ σε οδηγό γραφικών. Η ειλικρινής επιφύλαξη είναι η κάλυψη. Σύνθετα metafiles παραγμένα από εργαλεία σχεδίου θα χτυπήσουν τον μετρητή παραλειφθέντων εγγραφών, και η απάντηση σε αυτό είναι να αναδείξετε τον μετρητή και όχι να πλατύνετε τη whitelist σιωπηλά. Μια εικόνα που αποδίδεται μερικώς και το λέει είναι μια συζήτηση υποστήριξης· μια εικόνα που αποδίδεται λάθος και δεν λέει τίποτα είναι μια αναφορά σφάλματος από πελάτη
Το HotXLS χειρίζεται XLS, XLSX, ODS και CSV εγγενώς σε Delphi και C++Builder χωρίς εγκατεστημένο Excel, και η ίδια φιλοσοφία περιορισμένης ανάλυσης διατρέχει τα στρώματα container, τύπων και σχεδίων του. Οι λεπτομέρειες μορφών και ασφαλείας καταγράφονται στη σελίδα προϊόντος HotXLS Delphi spreadsheet component