Explorar el Código

Update archive detection

Lutz Roeder hace 4 años
padre
commit
69a66834f3
Se han modificado 7 ficheros con 84 adiciones y 59 borrados
  1. 1 1
      source/gzip.js
  2. 1 1
      source/paddle.js
  3. 40 13
      source/tar.js
  4. 26 25
      source/view.js
  5. 9 2
      source/zip.js
  6. 5 15
      test/models.js
  7. 2 2
      test/models.json

+ 1 - 1
source/gzip.js

@@ -11,7 +11,7 @@ gzip.Archive = class {
         if (stream.length > 18 && stream.peek(2).every((value, index) => value === signature[index])) {
             return new gzip.Archive(stream);
         }
-        throw new gzip.Error('Invalid gzip archive.');
+        return null;
     }
 
     constructor(stream) {

+ 1 - 1
source/paddle.js

@@ -774,7 +774,7 @@ paddle.Container = class {
                     let rootFolder = null;
                     for (const entry of this._data) {
                         const name = entry.name;
-                        if (name.startsWith('PaxHeader/') || (name.startsWith('.') && !name.startsWith('./'))) {
+                        if (name.startsWith('.') && !name.startsWith('./')) {
                             continue;
                         }
                         const parts = name.split('/');

+ 40 - 13
source/tar.js

@@ -7,21 +7,34 @@ tar.Archive = class {
     static open(buffer) {
         const stream = buffer instanceof Uint8Array ? new tar.BinaryReader(buffer) : buffer;
         if (stream.length > 512) {
-            return new tar.Archive(stream);
+            const buffer = stream.peek(512);
+            const sum = buffer.map((value, index) => (index >= 148 && index < 156) ? 32 : value).reduce((a, b) => a + b, 0);
+            let checksum = '';
+            for (let i = 148; i < 156 && buffer[i] !== 0x00; i++) {
+                checksum += String.fromCharCode(buffer[i]);
+            }
+            checksum = parseInt(checksum, 8);
+            if (!isNaN(checksum) && sum === checksum) {
+                return new tar.Archive(stream);
+            }
         }
-        throw new tar.Error('Invalid tar archive size.');
+        return null;
     }
 
     constructor(stream) {
         this._entries = [];
+        const position = stream.position;
         while (stream.position < stream.length) {
-            this._entries.push(new tar.Entry(stream));
+            const entry = new tar.Entry(stream);
+            if (entry.type === '0' || entry.type === '1' || entry.type === '2') {
+                this._entries.push(entry);
+            }
             if (stream.position + 512 > stream.length ||
                 stream.peek(512).every((value) => value === 0x00)) {
                 break;
             }
         }
-        stream.seek(0);
+        stream.seek(position);
     }
 
     get entries() {
@@ -34,26 +47,40 @@ tar.Entry = class {
     constructor(stream) {
         const buffer = stream.read(512);
         const reader = new tar.BinaryReader(buffer);
-        let sum = 0;
-        for (let i = 0; i < buffer.length; i++) {
-            sum += (i >= 148 && i < 156) ? 32 : buffer[i];
+        const sum = buffer.map((value, index) => (index >= 148 && index < 156) ? 32 : value).reduce((a, b) => a + b, 0);
+        let checksum = '';
+        for (let i = 148; i < 156 && buffer[i] !== 0x00; i++) {
+            checksum += String.fromCharCode(buffer[i]);
+        }
+        checksum = parseInt(checksum, 8);
+        if (isNaN(checksum) || sum !== checksum) {
+            throw new tar.Error('Invalid tar archive.');
         }
         this._name = reader.string(100);
         reader.string(8); // file mode
         reader.string(8); // owner
         reader.string(8); // group
-        const size = parseInt(reader.string(12).trim(), 8); // size
+        const size = parseInt(reader.string(12).trim(), 8);
         reader.string(12); // timestamp
-        const checksum = parseInt(reader.string(8).trim(), 8); // checksum
-        if (isNaN(checksum) || sum != checksum) {
-            throw new tar.Error('Invalid tar archive.');
-        }
-        reader.string(1); // link indicator
+        reader.string(8); // checksum
+        this._type = reader.string(1);
         reader.string(100); // name of linked file
+        if (reader.string(6) === 'ustar') {
+            reader.string(2); // ustar version
+            reader.string(32); // owner user name
+            reader.string(32); // owner group name
+            reader.string(8); // device major number
+            reader.string(8); // device number number
+            this._name = reader.string(155) + this._name;
+        }
         this._stream = stream.stream(size);
         stream.read(((size % 512) != 0) ? (512 - (size % 512)) : 0);
     }
 
+    get type() {
+        return this._type;
+    }
+
     get name() {
         return this._name;
     }

+ 26 - 25
source/view.js

@@ -1495,9 +1495,19 @@ view.ModelFactoryService = class {
     _unsupported(context) {
         const identifier = context.identifier;
         const extension = identifier.split('.').pop().toLowerCase();
-        const format = [ 'Zip', 'tar' ].find((extension) => context.entries(extension.toLowerCase()).length > 0);
-        if (format) {
-            throw new view.Error("Invalid file content. File contains " + format + " archive in '" + identifier + "'.", true);
+        for (const format of new Map([ [ 'Zip', zip ], [ 'tar', tar ] ])) {
+            const name = format[0];
+            const module = format[1];
+            let archive = null;
+            try {
+                archive = module.Archive.open(context.stream);
+            }
+            catch (error) {
+                // continue regardless of error
+            }
+            if (archive) {
+                throw new view.Error("Invalid file content. File contains " + name + " archive in '" + identifier + "'.", true);
+            }
         }
         const knownUnsupportedIdentifiers = new Set([
             'natives_blob.bin',
@@ -1589,11 +1599,11 @@ view.ModelFactoryService = class {
         let stream = context.stream;
         let extension;
         let identifier = context.identifier;
-        let buffer = stream.peek(Math.min(512, stream.length));
         try {
             extension = identifier.split('.').pop().toLowerCase();
-            if (extension === 'gz' || extension === 'tgz' || (buffer.length >= 18 && buffer[0] === 0x1f && buffer[1] === 0x8b)) {
-                const entries = gzip.Archive.open(stream).entries;
+            const gzipArchive = gzip.Archive.open(stream);
+            if (gzipArchive) {
+                const entries = gzipArchive.entries;
                 if (entries.length === 1) {
                     const entry = entries[0];
                     if (entry.name) {
@@ -1612,7 +1622,6 @@ view.ModelFactoryService = class {
                         }
                     }
                     stream = entry.stream;
-                    buffer = stream.peek(Math.min(512, stream.length));
                 }
             }
         }
@@ -1622,22 +1631,14 @@ view.ModelFactoryService = class {
         }
 
         try {
-            extension = identifier.split('.').pop().toLowerCase();
-            if (extension === 'zip' || (buffer.length > 2 && buffer[0] === 0x50 && buffer[1] === 0x4B)) {
-                entries.set('zip', zip.Archive.open(stream).entries);
-            }
-            if (extension === 'tar' || (buffer.length >= 512)) {
-                let sum = 0;
-                for (let i = 0; i < 512; i++) {
-                    sum += (i >= 148 && i < 156) ? 32 : buffer[i];
-                }
-                let checksum = '';
-                for (let i = 148; i < 156 && buffer[i] !== 0x00; i++) {
-                    checksum += String.fromCharCode(buffer[i]);
-                }
-                checksum = parseInt(checksum, 8);
-                if (!isNaN(checksum) && sum === checksum) {
-                    entries.set('tar', tar.Archive.open(stream).entries);
+            const formats = new Map([ [ 'zip', zip ], [ 'tar', tar ] ]);
+            for (const pair of formats) {
+                const format = pair[0];
+                const module = pair[1];
+                const archive = module.Archive.open(stream);
+                if (archive) {
+                    entries.set(format, archive.entries);
+                    break;
                 }
             }
         }
@@ -1692,10 +1693,10 @@ view.ModelFactoryService = class {
     _openEntries(entries) {
         try {
             const files = entries.map((entry) => entry.name).filter((file) => !file.endsWith('/') && !file.split('/').pop().startsWith('.')).slice();
-            const values = files.filter((file) => !file.startsWith('PaxHeader/') && (!file.startsWith('.') || file.startsWith('./')));
+            const values = files.filter((file) => !file.startsWith('.') || file.startsWith('./'));
             const map = values.map((file) => file.split('/').slice(0, -1));
             const at = index => list => list[index];
-            const rotate = list => list[0].map((item, index) => list.map(at(index)));
+            const rotate = list => list.length === 0 ? [] : list[0].map((item, index) => list.map(at(index)));
             const equals = list => list.every((item) => item === list[0]);
             const folder = rotate(map).filter(equals).map(at(0)).join('/');
             const rootFolder = folder.length === 0 ? folder : folder + '/';

+ 9 - 2
source/zip.js

@@ -14,11 +14,12 @@ zip.Archive = class {
         if (stream.length > 4 && stream.peek(2).every((value, index) => value === signature[index])) {
             return new zip.Archive(stream);
         }
-        throw new zip.Error('Invalid Zip archive.');
+        return null;
     }
 
     constructor(stream) {
         this._entries = [];
+        const position = stream.position;
         const lookup = (stream, signature) => {
             let position = stream.length;
             do {
@@ -123,7 +124,7 @@ zip.Archive = class {
         for (const entry of entries) {
             this._entries.push(new zip.Entry(stream, entry));
         }
-        stream.seek(0);
+        stream.seek(position);
     }
 
     get entries() {
@@ -509,6 +510,7 @@ zip.InflaterStream = class {
 
     constructor(stream, length) {
         this._stream = stream;
+        this._offset = this._stream.position;
         this._position = 0;
         this._length = length;
     }
@@ -568,9 +570,12 @@ zip.InflaterStream = class {
 
     _inflate() {
         if (this._buffer === undefined) {
+            const position = this._stream.position;
+            this._stream.seek(this._offset);
             const buffer = this._stream.peek();
             this._buffer = new zip.Inflater().inflateRaw(buffer, this._length);
             this._length = this._buffer.length;
+            this._stream.seek(position);
             delete this._stream;
         }
     }
@@ -652,8 +657,10 @@ zip.BinaryReader = class {
 zlib.Archive = class {
 
     constructor(stream) {
+        const position = stream.position;
         stream.read(2);
         this._entries = [ new zlib.Entry(stream) ];
+        stream.seek(position);
     }
 
     get entries() {

+ 5 - 15
test/models.js

@@ -360,21 +360,11 @@ function decompress(buffer) {
             buffer = entry.data;
         }
     }
-    if (buffer.length > 2 && buffer[0] === 0x50 && buffer[1] === 0x4B) {
-        return zip.Archive.open(buffer);
-    }
-    if (buffer.length >= 512) {
-        let sum = 0;
-        for (let i = 0; i < 512; i++) {
-            sum += (i >= 148 && i < 156) ? 32 : buffer[i];
-        }
-        let checksum = '';
-        for (let i = 148; i < 156 && buffer[i] !== 0x00; i++) {
-            checksum += String.fromCharCode(buffer[i]);
-        }
-        checksum = parseInt(checksum, 8);
-        if (!isNaN(checksum) && sum === checksum) {
-            return tar.Archive.open(buffer);
+    const formats = [ zip, tar ];
+    for (const module of formats) {
+        archive = module.Archive.open(buffer);
+        if (archive) {
+            break;
         }
     }
     return archive;

+ 2 - 2
test/models.json

@@ -58,7 +58,7 @@
     "type":   "_",
     "target": "gzip_invalid_archive.tar.gz",
     "source": "https://github.com/lutzroeder/netron/files/5468427/gzip_invalid_archive.tar.gz",
-    "error":  "Invalid gzip archive in 'gzip_invalid_archive.tar.gz'.",
+    "error":  "Unsupported file content (ff000000000000000000000000000000) for extension '.gz' in 'gzip_invalid_archive.tar.gz'.",
     "link":   "https://github.com/lutzroeder/netron/issues/249"
   },
   {
@@ -121,7 +121,7 @@
     "type":   "_",
     "target": "zip_invalid_archive.zip",
     "source": "https://github.com/lutzroeder/netron/files/5468425/zip_invalid_archive.zip",
-    "error":  "Invalid Zip archive in 'zip_invalid_archive.zip'.",
+    "error":  "Unsupported file content (ff000000000000000000000000000000) for extension '.zip' in 'zip_invalid_archive.zip'.",
     "link":   "https://github.com/lutzroeder/netron/issues/250"
   },
   {