{"record":{"id":"c235cc6c39121fc5","repo":"Tencent/tinker","slug":"not-a-zip-archive","errorCode":null,"errorMessage":"Not a zip archive","messagePattern":"Not a zip archive","errorType":"exception","errorClass":"ZipException","httpStatus":null,"severity":"error","filePath":"third-party/tinker-ziputils/src/main/java/com/tencent/tinker/ziputils/ziputil/TinkerZipFile.java","lineNumber":428,"sourceCode":"     *\n     * <p>This is all a little wobbly.  If the wrong value ends up in the EOCD\n     * area, we're hosed. This appears to be the way that everybody handles\n     * it though, so we're in good company if this fails.\n     */\n    private void readCentralDir() throws IOException {\n        // Scan back, looking for the End Of Central Directory field. If the zip file doesn't\n        // have an overall comment (unrelated to any per-entry comments), we'll hit the EOCD\n        // on the first try.\n        // No need to synchronize raf here -- we only do this when we first open the zip file.\n        long scanOffset = raf.length() - ENDHDR;\n        if (scanOffset < 0) {\n            throw new ZipException(\"File too short to be a zip file: \" + raf.length());\n        }\n\n        raf.seek(0);\n        final int headerMagic = Integer.reverseBytes(raf.readInt());\n        if (headerMagic != LOCSIG) {\n            throw new ZipException(\"Not a zip archive\");\n        }\n\n        long stopOffset = scanOffset - 65536;\n        if (stopOffset < 0) {\n            stopOffset = 0;\n        }\n\n        while (true) {\n            raf.seek(scanOffset);\n            if (Integer.reverseBytes(raf.readInt()) == ENDSIG) {\n                break;\n            }\n\n            scanOffset--;\n            if (scanOffset < stopOffset) {\n                throw new ZipException(\"End Of Central Directory signature not found\");\n            }\n        }","sourceCodeStart":410,"sourceCodeEnd":446,"githubUrl":"https://github.com/Tencent/tinker/blob/1b7ea02c239840f563ea64fb5bd286eb98d4011e/third-party/tinker-ziputils/src/main/java/com/tencent/tinker/ziputils/ziputil/TinkerZipFile.java#L410-L446","documentation":"After the minimum-length check, readCentralDir seeks to offset 0 and reads the first 4 bytes; if they are not the 'PK\\x03\\x04' local-file-header signature (LOCSIG), the file cannot be a zip at all and the constructor throws ZipException('Not a zip archive'). This is a content check, independent of the file extension — a .zip named file with non-zip contents fails here.","triggerScenarios":"Opening a file whose first 4 bytes are not 'PK\\x03\\x04': tar/gz/rar archives renamed to .zip, raw binaries, text/HTML content, or a zip with a prepended stub in unusual cases where the EOCD scan would have succeeded but the head check does not.","commonSituations":"MIME-type confusion in an upload pipeline; a mirror/CDN serving a decompressed or transformed artifact; users renaming files to force them through a zip-based flow; build outputs configured with the wrong archiver.","solutions":["Identify the real format from the magic bytes (file(1), ContentType sniffing) and route it to the correct handler.","Regenerate/re-download the artifact from a source that actually produces zip.","Add a content-type check at ingestion so non-zip payloads are rejected before reaching TinkerZipFile."],"exampleFix":"// before\nTinkerZipFile zf = new TinkerZipFile(new File(path));\n\n// after\nbyte[] magic = new byte[4];\ntry (RandomAccessFile r = new RandomAccessFile(path, \"r\")) {\n    r.readFully(magic);\n}\nif (!Arrays.equals(magic, new byte[]{'P','K',3,4})) {\n    throw new IOException(path + \" is not a zip archive\");\n}\nTinkerZipFile zf = new TinkerZipFile(new File(path));","handlingStrategy":"validation","validationCode":"static boolean looksLikeZip(File f) throws IOException {\n    if (f.length() < 4) return false;\n    try (RandomAccessFile r = new RandomAccessFile(f, \"r\")) {\n        byte[] b = new byte[4];\n        r.readFully(b);\n        return b[0]=='P' && b[1]=='K' && b[2]==3 && b[3]==4;\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Check magic bytes at ingestion; never trust file extensions.","Verify Content-Type when artifacts arrive over HTTP.","For self-extracting or stub-prefixed archives, strip the stub before this library reads them."],"tags":["zip","format-check","corruption","tinker"],"backgroundTag":null,"analyzedSha":"1b7ea02c239840f563ea64fb5bd286eb98d4011e","analyzedAt":"2026-08-14T15:16:52.110Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}