{"thread":{"id":"66464","subject":"[BUG] ZIP timestamp conversion and strict fast-import date validation","startedAt":"2026-10-05T13:48:20Z","lastAt":"2026-10-06T03:22:36Z","messageCount":4,"participants":["Matthew E. Luallen","René Scharfe","m@sph3r3.com"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"554178","messageId":"CA+h9NxRT-9QzLGihdL_Bp-yyt1AdXJ79YYgpK-OaegUz8e+HcA@mail.gmail.com","threadId":"66464","inReplyTo":null,"subject":"[BUG] ZIP timestamp conversion and strict fast-import date validation","fromName":"Matthew E. Luallen","fromEmail":"m@sph3r3.com","sentAt":"2026-10-05T13:48:20Z","receivedAt":"2026-10-05T13:48:20Z","isPatch":false,"body":"Hello Git community,\n\nI'm reporting three date-handling bugs reproduced on Apple Git 2.50.1\nand upstream Git 2.56.0:\n\n1. Exporting a 1972-dated commit with git archive --format=zip produces\n   a legacy DOS date interpreted as 2100, while the extended Unix\n   timestamp retains 1972.\n2. Exporting a commit at epoch 4294967296 (2106-02-07 06:28:16 UTC)\n   wraps ZIP's four-byte extended timestamp to zero (1970). Exporting\n   the same commit as TAR preserves the original value.\n3. git fast-import --date-format=raw accepts -32184000 +0000, but\n   git fsck --strict then reports badDate and ISO rendering returns\n   literal placeholders. This occurs in strict raw mode, not just\n   the deliberately permissive import mode.\n\nFor the archive cases, export the dated commit with:\n\n    git archive --format=zip <commit> > test.zip\n    git archive --format=tar <commit> > test.tar\n\nCompare the ZIP DOS and extended timestamp fields with the TAR mtime.\nThe overflow affects consumer behavior: in macOS tests, UnZip update\nmode retained different existing 2025 content because the 2106 ZIP\nappeared older. An in-range 2038 ZIP replaced that content.\n\nWhat range-handling policy should ZIP use, and should strict raw import\nreject timestamps that fsck considers invalid?\n\nThank you,\nMatthew E. Luallen (@meluallen)\nWith research, reproduction, and drafting assistance from OpenAI Codex.\n\n"},{"id":"554217","messageId":"f8dc40a4-920d-4dc5-9f71-7686bfc6255b@web.de","threadId":"66464","inReplyTo":"CA+h9NxRT-9QzLGihdL_Bp-yyt1AdXJ79YYgpK-OaegUz8e+HcA@mail.gmail.com","subject":"Re: [BUG] ZIP timestamp conversion and strict fast-import date validation","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2026-10-05T19:49:33Z","receivedAt":"2026-10-05T19:49:33Z","isPatch":false,"body":"On 10/5/26 3:48 PM, Matthew E. Luallen wrote:\n> Hello Git community,\n> \n> I'm reporting three date-handling bugs reproduced on Apple Git 2.50.1\n> and upstream Git 2.56.0:\n> \n> 1. Exporting a 1972-dated commit with git archive --format=zip produces\n>    a legacy DOS date interpreted as 2100, while the extended Unix\n>    timestamp retains 1972.\n\nDOS timestamps can express years starting from 1980.  Unix timestamps\nstart at 1970.  We can't provide a DOS timestamp for 1972, but can we do\nbetter?  zip(1) clamps DOS timestamps to zero = the DOS epoch =\n1980-01-01 00:00:00.\n\nIs anything using the DOS timestamp even though a Unix timestamp is\npresent?\n\n> 2. Exporting a commit at epoch 4294967296 (2106-02-07 06:28:16 UTC)\n>    wraps ZIP's four-byte extended timestamp to zero (1970). Exporting\n>    the same commit as TAR preserves the original value.\n\ntar's timestamp can range from 1970 to 2242 with standard headers and\nbeyond indefinitely with extended headers.\n\nWe cannot put a higher value than 4294967295 into the four-byte field\nprovided by the Unix time extension for ZIP, but we could clamp to that\nvalue.  zip(1) wraps around as well, though..\n\n(I'm using \"Zip 3.0 (July 5th 2008), by Info-ZIP, with modifications by\nApple Inc.\")\n\n> 3. git fast-import --date-format=raw accepts -32184000 +0000, but\n>    git fsck --strict then reports badDate and ISO rendering returns\n>    literal placeholders. This occurs in strict raw mode, not just\n>    the deliberately permissive import mode.\n\nWell, raw mode passes on the timestamp value with only little checks.\nstrtoul(3) used in builtin/fast-import.c::validate_raw_date() happily\naccepts negative numbers.  The latter function contains two NEEDSWORK\ncomments about perhaps adding more checks, though.\n\nRené\n\n\n"},{"id":"554221","messageId":"CA+h9NxQEJaKrbTXUeryEhNMfOfuU0EykiJRi3PaJE9dRFo5tWw@mail.gmail.com","threadId":"66464","inReplyTo":"f8dc40a4-920d-4dc5-9f71-7686bfc6255b@web.de","subject":"Re: [BUG] ZIP timestamp conversion and strict fast-import date validation","fromName":"Matthew E. Luallen","fromEmail":"m@sph3r3.com","sentAt":"2026-10-05T20:32:32Z","receivedAt":"2026-10-05T20:32:32Z","isPatch":false,"body":"Hi René,\n\nThank you for the thoughtful response. I appreciate your help.\n\n- Reader example: Python 3.14.7's ZipInfo.date_time reports 2100 for\n  the 1972 ZIP despite its correct Unix timestamp. Our tested UnZip\n  and bsdtar restored 1972 correctly; this is a metadata-reading example.\n\n- Separate consequence: the 2106-to-1970 wrap caused UnZip update mode\n  to retain different existing 2025 content. The 2038 control updated\n  correctly. No deployed security bypass has been demonstrated.\n\n- Fix direction: would you prefer clamping or rejecting ZIP timestamps\n  beyond the Unix field's range? Our candidate rejects them. Would\n  rejecting negative dates in strict raw import be a useful first step?\n\nAttached is brief guidance on misinterpretation risks, clearer date\nlabels, and safer downstream decisions. These suggestions complement\nthe ordinary bug fixes without asserting a security classification.\n\nBest,\nMatt\n\n\nGIT TIMESTAMP GUIDANCE: INTERPRETATION, INTEGRITY AND REMEDIATION\n5 October 2026 | Companion to the public Git date-handling discussion\n\nWHY IT MATTERS\n\n- Interpretation: dates can be mistaken for creation, publication or\n  approval times. Conversion errors can mislead without malicious intent.\n- Scale: repeated reliance on dates across update, review and audit\n  pipelines could multiply errors. Prevalence has not been measured.\n- Security impact requires attacker-influenced metadata to affect a\n  security decision without independent checks. A local update decision\n  changed; a production security bypass has not been demonstrated.\n\nOBSERVED, WITH CONTROLS\n\n- Python 3.14.7 reads the 1972 ZIP's DOS date as 2100 despite the correct\n  Unix extra field. UnZip 6.00 and bsdtar 3.5.3 restored 1972; Python's\n  ordinary extraction used a current local modification time.\n- A 2106 commit's ZIP Unix timestamp wrapped to 1970; UnZip update mode\n  kept different existing 2025 bytes. A 2038 control replaced them;\n  TAR preserved 2106. Strict raw import also accepted a negative date\n  subsequently rejected by strict fsck. Reader behavior varies by platform.\n\nPRACTICAL OPTIONS\n\n- Exporters: validate ranges before encoding. Clamp pre-1980 DOS dates\n  while preserving representable Unix dates. For Unix-field overflow,\n  choose rejection or documented clamping; clamping loses the original\n  value. Our candidate rejects overflow. These are proposed policies.\n- Importers: align strict parsing with validation; check negative values,\n  complete numeric input and overflow. Keep permissive modes explicit.\n  Test boundaries across timezones and archive readers.\n- Consumers: decide updates using expected content/version information\n  from a trusted source, not modification time alone. For change review,\n  retain a trusted baseline commit and compare reachable changes; handle\n  missing or rewritten history explicitly. Date windows do not establish\n  complete coverage of newly received content.\n- Displays: distinguish 'committer-supplied date', 'archive modification\n  time' and 'server observed at'. Explain clamping and missing evidence.\n  Server receipt is not original creation or first publication elsewhere.\n- Evidence: preserve original bytes and independent receipt records.\n  Commit signatures bind signed content to a key under a trust policy;\n  they do not independently prove its claimed creation time. A validated\n  trusted timestamp can support existence by a time, not exact creation.\n\nRISK TAXONOMY, NOT A VULNERABILITY ASSIGNMENT\n\n- Numeric representation/truncation: CWE-197; wraparound: CWE-190.\n- Validation: CWE-20 (broad category). Downstream trust: CWE-807 only\n  where a security decision relies on untrusted input. Display confusion\n  alone does not establish it. These labels do not establish severity.\n\nREFERENCES AND EVIDENCE\n\nGit date controls: https://git-scm.com/docs/git-commit#_commit_information\nArchive behavior: https://git-scm.com/docs/git-archive#_description\nCWE taxonomy: https://cwe.mitre.org/data/definitions/197.html\nhttps://cwe.mitre.org/data/definitions/190.html\nhttps://cwe.mitre.org/data/definitions/20.html\nhttps://cwe.mitre.org/data/definitions/807.html\nTrusted timestamp semantics: https://www.rfc-editor.org/rfc/rfc3161\nEvidence: recorded 2026-10-01 consumer-date-impact results and Git matrix;\nno new experiment is claimed by this guidance.\n\nMatthew E. Luallen (@meluallen), with research, reproduction and drafting\nassistance from OpenAI Codex.\n"},{"id":"554230","messageId":"CA+h9NxSzuNNQZ0pKKso9Y8BkXKZYgrGFLL050Pi3O7PC_gi65g@mail.gmail.com","threadId":"66464","inReplyTo":"f8dc40a4-920d-4dc5-9f71-7686bfc6255b@web.de","subject":"Re: [BUG] ZIP timestamp conversion and strict fast-import date validation","fromName":"","fromEmail":"m@sph3r3.com","sentAt":"2026-10-06T03:22:36Z","receivedAt":"2026-10-06T03:22:36Z","isPatch":false,"body":"Hi René,\n\nThanks for the explanation. Python 3.14.7’s ZipInfo.date_time is one\nexample: it reads the DOS date as 2100 for the 1972 archive, even\nthough the correct Unix timestamp is present. This affects metadata\nreading; the tested UnZip and bsdtar restored 1972 correctly.\n\nThe 2106 case is separate: the Unix timestamp wraps to 1970, causing\nUnZip update mode to retain different existing 2025 content. The 2038\ncontrol replaced that content as expected.\n\nBest,\nMatthew\n\nOn Mon, 05 Oct 2026 21:49:33 +0200, René Scharfe <l.s.r@web.de> wrote:\n> On 10/5/26 3:48 PM, Matthew E. Luallen wrote:\n> > Hello Git community,\n> >\n> > I'm reporting three date-handling bugs reproduced on Apple Git 2.50.1\n> > and upstream Git 2.56.0:\n> >\n> > 1. Exporting a 1972-dated commit with git archive --format=zip produces\n> >    a legacy DOS date interpreted as 2100, while the extended Unix\n> >    timestamp retains 1972.\n>\n> DOS timestamps can express years starting from 1980.  Unix timestamps\n> start at 1970.  We can't provide a DOS timestamp for 1972, but can we do\n> better?  zip(1) clamps DOS timestamps to zero = the DOS epoch =\n> 1980-01-01 00:00:00.\n>\n> Is anything using the DOS timestamp even though a Unix timestamp is\n> present?\n>\n> > 2. Exporting a commit at epoch 4294967296 (2106-02-07 06:28:16 UTC)\n> >    wraps ZIP's four-byte extended timestamp to zero (1970). Exporting\n> >    the same commit as TAR preserves the original value.\n>\n> tar's timestamp can range from 1970 to 2242 with standard headers and\n> beyond indefinitely with extended headers.\n>\n> We cannot put a higher value than 4294967295 into the four-byte field\n> provided by the Unix time extension for ZIP, but we could clamp to that\n> value.  zip(1) wraps around as well, though..\n>\n> (I'm using \"Zip 3.0 (July 5th 2008), by Info-ZIP, with modifications by\n> Apple Inc.\")\n>\n> > 3. git fast-import --date-format=raw accepts -32184000 +0000, but\n> >    git fsck --strict then reports badDate and ISO rendering returns\n> >    literal placeholders. This occurs in strict raw mode, not just\n> >    the deliberately permissive import mode.\n>\n> Well, raw mode passes on the timestamp value with only little checks.\n> strtoul(3) used in builtin/fast-import.c::validate_raw_date() happily\n> accepts negative numbers.  The latter function contains two NEEDSWORK\n> comments about perhaps adding more checks, though.\n>\n> René\n\n"}]}