{"record":{"id":"e227b6498ea381d3","repo":"Tencent/tinker","slug":"invalid-general-purpose-bit-flag-gpbf-e227b6","errorCode":null,"errorMessage":"Invalid General Purpose Bit Flag: ${gpbf}","messagePattern":"Invalid General Purpose Bit Flag: (.+?)","errorType":"exception","errorClass":"ZipException","httpStatus":null,"severity":"error","filePath":"third-party/tinker-ziputils/src/main/java/com/tencent/tinker/ziputils/ziputil/TinkerZipFile.java","lineNumber":355,"sourceCode":"            return null;\n        }\n        // Create an InputStream at the right part of the file.\n        RandomAccessFile localRaf = raf;\n        synchronized (localRaf) {\n            // We don't know the entry data's start position. All we have is the\n            // position of the entry's local header.\n            // http://www.pkware.com/documents/casestudies/APPNOTE.TXT\n            RAFStream rafStream = new RAFStream(localRaf, entry.localHeaderRelOffset);\n            DataInputStream is = new DataInputStream(rafStream);\n            final int localMagic = Integer.reverseBytes(is.readInt());\n            if (localMagic != LOCSIG) {\n                throwZipException(filename, localRaf.length(), entry.getName(), entry.localHeaderRelOffset, \"Local File Header\", localMagic);\n            }\n            is.skipBytes(2);\n            // At position 6 we find the General Purpose Bit Flag.\n            int gpbf = Short.reverseBytes(is.readShort()) & 0xffff;\n            if ((gpbf & TinkerZipFile.GPBF_UNSUPPORTED_MASK) != 0) {\n                throw new ZipException(\"Invalid General Purpose Bit Flag: \" + gpbf);\n            }\n            // Offset 26 has the file name length, and offset 28 has the extra field length.\n            // These lengths can differ from the ones in the central header.\n            is.skipBytes(18);\n            int fileNameLength = Short.reverseBytes(is.readShort()) & 0xffff;\n            int extraFieldLength = Short.reverseBytes(is.readShort()) & 0xffff;\n            is.close();\n            // Skip the variable-size file name and extra field data.\n            rafStream.skip(fileNameLength + extraFieldLength);\n            /*if (entry.compressionMethod == ZipEntry.STORED) {\n                rafStream.endOffset = rafStream.offset + entry.size;\n                return rafStream;\n            } else {\n                rafStream.endOffset = rafStream.offset + entry.compressedSize;\n                int bufSize = Math.max(1024, (int) Math.min(entry.getSize(), 65535L));\n                return new ZipInflaterInputStream(rafStream, new Inflater(true), bufSize, entry);\n            }*/\n            if (entry.compressionMethod == TinkerZipEntry.STORED) {","sourceCodeStart":337,"sourceCodeEnd":373,"githubUrl":"https://github.com/Tencent/tinker/blob/1b7ea02c239840f563ea64fb5bd286eb98d4011e/third-party/tinker-ziputils/src/main/java/com/tencent/tinker/ziputils/ziputil/TinkerZipFile.java#L337-L373","documentation":"Before returning an input stream, TinkerZipFile re-reads the entry's local file header and checks the General Purpose Bit Flag against GPBF_UNSUPPORTED_MASK, which in this fork equals GPBF_ENCRYPTED_FLAG. Any entry whose local header marks it as encrypted causes ZipException('Invalid General Purpose Bit Flag: <gpbf>') — this library performs no decryption. Note it reads the flag from the local header, so the central directory's flags are not what triggers it.","triggerScenarios":"Calling getInputStream() on an entry that was encrypted when the zip was created (password-protected zip, or an APK whose entries were encrypted by a signing/packing tool that sets bit 0 of the GPBF in the local header).","commonSituations":"Feeding a password-protected archive to a pipeline that expects plain zips; 'app reinforcement' tools that encrypt dex/resources inside the APK; archives produced by Windows Explorer's 'Encrypt' option or 7-Zip AES.","solutions":["Re-create the archive without entry encryption (zip without a password / repackage the APK before the encryption step).","If encryption is intentional, decrypt the archive first with a tool that supports it and process the decrypted copy.","Pre-scan archives for encrypted entries (central directory bit 0) and route them away from TinkerZipFile with a clear user-facing error."],"exampleFix":"// before\nInputStream is = zf.getInputStream(entry); // throws if entry is encrypted\n\n// after\nif ((entry.getGeneralPurposeBit() & 0x1) != 0) { // central-dir hint\n    throw new IOException(\"Archive contains encrypted entry: \" + entry.getName());\n}\nInputStream is = zf.getInputStream(entry);","handlingStrategy":"validation","validationCode":"// central directory flag bit 0 == encrypted; cheap pre-scan of the entry set\nstatic boolean hasEncryptedEntries(TinkerZipFile zf) {\n    Enumeration<? extends TinkerZipEntry> en = zf.entries();\n    while (en.hasMoreElements()) {\n        TinkerZipEntry e = en.nextElement();\n        if ((e.getGeneralPurposeBit() & 0x1) != 0) {\n            return true;\n        }\n    }\n    return false;\n}","typeGuard":null,"tryCatchPattern":"try {\n    return zf.getInputStream(entry);\n} catch (ZipException e) {\n    if (e.getMessage().startsWith(\"Invalid General Purpose Bit Flag\")) {\n        throw new IOException(\"Entry '\" + entry.getName() + \"' is encrypted; decrypt the archive first\", e);\n    }\n    throw e;\n}","preventionTips":["Produce archives unencrypted anywhere TinkerZipFile will read them.","Pre-scan entries for the encryption flag and fail with an actionable message.","Beware APK 'reinforcement' tools that encrypt entry payloads — run Tinker before that stage."],"tags":["zip","encryption","unsupported-feature","tinker"],"backgroundTag":null,"analyzedSha":"1b7ea02c239840f563ea64fb5bd286eb98d4011e","analyzedAt":"2026-08-14T15:16:52.110Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}