Before accepting an AI-written ZIP importer, check that the bytes sent to the business parser are the bytes that passed validation. A filename is not always enough to establish that identity: an archive can contain multiple members with the same name.
For a routine supplier upload, the simplest policy may be to reject duplicate member names and require a corrected archive. Where repeated names are genuinely supported, preserve the selected member’s identity through validation and processing. Do not validate an entry obtained from a listing and later retrieve it again by name without checking what that lookup means.
This guide develops that review question using three harmless, synthetic archives executed locally with Python 3.12.14. It does not report an assistant benchmark, independent testing of a model, or a complete secure archive processor.
Agree on the upload contract before reviewing the code
Imagine a fictional invoice-import workflow that accepts a ZIP containing exactly one regular CSV member named invoices.csv. The CSV must have the columns invoice_id,amount, and the import must preserve the invoice identifiers. The example amounts and identifiers below are invented; no customer data is involved.
Write this contract down before asking an assistant to implement it. “Read invoices.csv from the upload” leaves unanswered whether multiple copies are permitted, which copy wins, and what happens when another component makes a different choice. A reviewer should be able to answer those questions without relying on the order in which a particular library happens to return entries.
This is a different boundary from colliding CSV column headers. Here the problem occurs before CSV parsing: the archive can offer more than one candidate for the supposedly singular input file. A perfect column validator cannot protect a different payload that it never inspected.
Treat archive acceptance, member selection and payload validation as separate decisions. A readable archive does not necessarily satisfy the upload contract. A correctly named member does not necessarily contain an acceptable CSV. An acceptable CSV does not authorize a database write.
A ZIP member name can identify more than one entry
The Python zipfile documentation explicitly notes archives with duplicate member names. Its open, read and extract methods accept a ZipInfo object as well as a filename, and infolist() supplies objects describing individual members. That distinction lets code retain a particular entry instead of resolving its name again.
The documentation also defines testzip() as checking file CRCs and headers. It is not an invoice-schema validator or a declaration that member names meet your application’s uniqueness rule.
A review should therefore follow the data, not just find a validation function somewhere in the code. Identify how the member is chosen, how its bytes are checked, and how those bytes reach the consumer. Watch especially for a handoff that discards the selected entry and keeps only its name.
The synthetic case: validation passes on one payload, lookup returns another
We created three in-memory ZIP archives using Python’s standard library. The first contained one invoices.csv. The second contained two members with that exact name but different harmless contents. The third contained two identically named members with identical contents.
The first payload was:
invoice_id,amount
INV-1,10
The alternative payload was:
invoice_id,note
INV-2,hold
For each archive, we selected the first object returned by infolist() and read it using that object. We then compared those bytes with a second read using only its filename. These are the operations under examination:
selected = archive.infolist()[0]
checked_bytes = archive.read(selected)
later_bytes = archive.read(selected.filename)
This excerpt demonstrates the identity check, not a production selection policy. Choosing the first member is intentionally part of the fixture, rather than a recommendation for arbitrary uploads.
| Synthetic archive | Header read through the selected object | Header returned by the later name lookup | Same bytes? |
|---|---|---|---|
| One member | invoice_id,amount | invoice_id,amount | Yes |
| Two same-name members, different contents | invoice_id,amount | invoice_id,note | No |
| Two same-name members, identical contents | invoice_id,amount | invoice_id,amount | Yes |
Reading the original selected object again returned the original bytes in all three cases. testzip() returned no bad member for any of the archives. An explicit exact-name uniqueness check accepted the single-member archive and rejected both duplicate-member archives.
The result is narrow but useful. In the different-content case, validating the first header would not validate the header received through the later name lookup. No corrupt compression or filesystem extraction was needed to reproduce that mismatch. We did not submit these archives to a commercial assistant, upload service or production importer.
The identical-content case matters too. A test that checks only whether the final CSV looks correct would miss the contract violation. For this fictional one-member workflow, duplicates remain invalid even when today’s contents happen to match. Otherwise, acceptance would depend on a coincidence rather than a defined selection rule.
Keep selection and consumption tied together
Our proposed review sequence starts by enumerating the archive’s members and checking the upload contract before processing the CSV. For this example, reject unexpected member counts, unexpected names and duplicates. Report the ambiguity so the supplier can fix the package; silently dropping one copy conceals a defect in the input.
After acceptance, obtain the allowed member and validate the payload that will actually be processed. For a small, strictly size-bounded file, one straightforward design is to retain the bytes, validate them and pass those same bytes to the CSV parser. A streaming design needs an equally explicit connection between validation and consumption; reopening an upload or resolving a name again should trigger review.
Keep the source archive stable during that sequence. If another component can replace it, member identity inside the original archive does not establish identity inside the replacement. Record enough provenance to investigate a failed import, while following the organization’s rules for sensitive invoice data and logging.
If the workflow intentionally permits repeated names, require an explicit specification for selecting them and tests for every stage that makes that choice. Passing a selected ZipInfo object addresses this fixture’s relookup problem within the same open archive. It does not establish agreement with another library, scanner or downstream extraction tool.
Research supports checking interpretation across components
The original USENIX Security 2025 paper, My ZIP isn’t your ZIP, studies disagreements between ZIP parsers using differential fuzzing. Its duplicate-file discussion explains that retrieving a path shared by multiple entries requires a choice. It also examines how path normalization can create disagreements about which entries share a path.
We read the study’s methods, duplicate-file analysis and limitations. Its comparison uses filesystem outputs, which the authors caution may differ from internal parsing state. The paper is evidence that interpretation matters across components; it does not validate our proposed importer or establish which current product versions remain affected. Our in-memory Python fixture is a separate, much smaller observation.
Member identity is one gate in a larger archive policy
Do not describe a duplicate-name check as making an archive safe. Snyk’s original Zip Slip practitioner report, publicly disclosed on 5 June 2018, describes a different failure: archive paths can cause extraction to write outside the intended directory. Our fixture performs no filesystem extraction and does not test that risk.
A production review also needs explicit decisions about supported formats, allowed paths, size and resource limits, encrypted or unsupported members, destination collisions and failure handling. When content will be extracted or processed by another component, test that actual route. Exact string uniqueness alone cannot establish agreement after another component transforms filenames.
For the next importer review, add both duplicate fixtures to the acceptance cases: different payloads to expose relookup, identical payloads to test policy enforcement. Check the business parser’s actual input and confirm that rejection produces no committed business changes. As with failed report refreshes, detecting a problem and preventing its side effects need separate evidence.