# [BUG] ZIP timestamp conversion and strict fast-import date validation

4 messages from 2026-10-05 to 2026-10-06. Participants: Matthew E. Luallen, René Scharfe, m@sph3r3.com.
Thread: https://gitlist.dev/t/66464

## Matthew E. Luallen, 2026-10-05 13:48

Subject: [BUG] ZIP timestamp conversion and strict fast-import date validation
Message-ID: <CA+h9NxRT-9QzLGihdL_Bp-yyt1AdXJ79YYgpK-OaegUz8e+HcA@mail.gmail.com>

```
Hello Git community,

I'm reporting three date-handling bugs reproduced on Apple Git 2.50.1
and upstream Git 2.56.0:

1. Exporting a 1972-dated commit with git archive --format=zip produces
   a legacy DOS date interpreted as 2100, while the extended Unix
   timestamp retains 1972.
2. Exporting a commit at epoch 4294967296 (2106-02-07 06:28:16 UTC)
   wraps ZIP's four-byte extended timestamp to zero (1970). Exporting
   the same commit as TAR preserves the original value.
3. git fast-import --date-format=raw accepts -32184000 +0000, but
   git fsck --strict then reports badDate and ISO rendering returns
   literal placeholders. This occurs in strict raw mode, not just
   the deliberately permissive import mode.

For the archive cases, export the dated commit with:

    git archive --format=zip <commit> > test.zip
    git archive --format=tar <commit> > test.tar

Compare the ZIP DOS and extended timestamp fields with the TAR mtime.
The overflow affects consumer behavior: in macOS tests, UnZip update
mode retained different existing 2025 content because the 2106 ZIP
appeared older. An in-range 2038 ZIP replaced that content.

What range-handling policy should ZIP use, and should strict raw import
reject timestamps that fsck considers invalid?

Thank you,
Matthew E. Luallen (@meluallen)
With research, reproduction, and drafting assistance from OpenAI Codex.


```

## René Scharfe, 2026-10-05 19:49

Subject: Re: [BUG] ZIP timestamp conversion and strict fast-import date validation
Message-ID: <f8dc40a4-920d-4dc5-9f71-7686bfc6255b@web.de>
In-Reply-To: <CA+h9NxRT-9QzLGihdL_Bp-yyt1AdXJ79YYgpK-OaegUz8e+HcA@mail.gmail.com>

```
On 10/5/26 3:48 PM, Matthew E. Luallen wrote:
> Hello Git community,
> 
> I'm reporting three date-handling bugs reproduced on Apple Git 2.50.1
> and upstream Git 2.56.0:
> 
> 1. Exporting a 1972-dated commit with git archive --format=zip produces
>    a legacy DOS date interpreted as 2100, while the extended Unix
>    timestamp retains 1972.

DOS timestamps can express years starting from 1980.  Unix timestamps
start at 1970.  We can't provide a DOS timestamp for 1972, but can we do
better?  zip(1) clamps DOS timestamps to zero = the DOS epoch =
1980-01-01 00:00:00.

Is anything using the DOS timestamp even though a Unix timestamp is
present?

> 2. Exporting a commit at epoch 4294967296 (2106-02-07 06:28:16 UTC)
>    wraps ZIP's four-byte extended timestamp to zero (1970). Exporting
>    the same commit as TAR preserves the original value.

tar's timestamp can range from 1970 to 2242 with standard headers and
beyond indefinitely with extended headers.

We cannot put a higher value than 4294967295 into the four-byte field
provided by the Unix time extension for ZIP, but we could clamp to that
value.  zip(1) wraps around as well, though..

(I'm using "Zip 3.0 (July 5th 2008), by Info-ZIP, with modifications by
Apple Inc.")

> 3. git fast-import --date-format=raw accepts -32184000 +0000, but
>    git fsck --strict then reports badDate and ISO rendering returns
>    literal placeholders. This occurs in strict raw mode, not just
>    the deliberately permissive import mode.

Well, raw mode passes on the timestamp value with only little checks.
strtoul(3) used in builtin/fast-import.c::validate_raw_date() happily
accepts negative numbers.  The latter function contains two NEEDSWORK
comments about perhaps adding more checks, though.

René



```

## Matthew E. Luallen, 2026-10-05 20:32

Subject: Re: [BUG] ZIP timestamp conversion and strict fast-import date validation
Message-ID: <CA+h9NxQEJaKrbTXUeryEhNMfOfuU0EykiJRi3PaJE9dRFo5tWw@mail.gmail.com>
In-Reply-To: <f8dc40a4-920d-4dc5-9f71-7686bfc6255b@web.de>

```
Hi René,

Thank you for the thoughtful response. I appreciate your help.

- Reader example: Python 3.14.7's ZipInfo.date_time reports 2100 for
  the 1972 ZIP despite its correct Unix timestamp. Our tested UnZip
  and bsdtar restored 1972 correctly; this is a metadata-reading example.

- Separate consequence: the 2106-to-1970 wrap caused UnZip update mode
  to retain different existing 2025 content. The 2038 control updated
  correctly. No deployed security bypass has been demonstrated.

- Fix direction: would you prefer clamping or rejecting ZIP timestamps
  beyond the Unix field's range? Our candidate rejects them. Would
  rejecting negative dates in strict raw import be a useful first step?

Attached is brief guidance on misinterpretation risks, clearer date
labels, and safer downstream decisions. These suggestions complement
the ordinary bug fixes without asserting a security classification.

Best,
Matt


GIT TIMESTAMP GUIDANCE: INTERPRETATION, INTEGRITY AND REMEDIATION
5 October 2026 | Companion to the public Git date-handling discussion

WHY IT MATTERS

- Interpretation: dates can be mistaken for creation, publication or
  approval times. Conversion errors can mislead without malicious intent.
- Scale: repeated reliance on dates across update, review and audit
  pipelines could multiply errors. Prevalence has not been measured.
- Security impact requires attacker-influenced metadata to affect a
  security decision without independent checks. A local update decision
  changed; a production security bypass has not been demonstrated.

OBSERVED, WITH CONTROLS

- Python 3.14.7 reads the 1972 ZIP's DOS date as 2100 despite the correct
  Unix extra field. UnZip 6.00 and bsdtar 3.5.3 restored 1972; Python's
  ordinary extraction used a current local modification time.
- A 2106 commit's ZIP Unix timestamp wrapped to 1970; UnZip update mode
  kept different existing 2025 bytes. A 2038 control replaced them;
  TAR preserved 2106. Strict raw import also accepted a negative date
  subsequently rejected by strict fsck. Reader behavior varies by platform.

PRACTICAL OPTIONS

- Exporters: validate ranges before encoding. Clamp pre-1980 DOS dates
  while preserving representable Unix dates. For Unix-field overflow,
  choose rejection or documented clamping; clamping loses the original
  value. Our candidate rejects overflow. These are proposed policies.
- Importers: align strict parsing with validation; check negative values,
  complete numeric input and overflow. Keep permissive modes explicit.
  Test boundaries across timezones and archive readers.
- Consumers: decide updates using expected content/version information
  from a trusted source, not modification time alone. For change review,
  retain a trusted baseline commit and compare reachable changes; handle
  missing or rewritten history explicitly. Date windows do not establish
  complete coverage of newly received content.
- Displays: distinguish 'committer-supplied date', 'archive modification
  time' and 'server observed at'. Explain clamping and missing evidence.
  Server receipt is not original creation or first publication elsewhere.
- Evidence: preserve original bytes and independent receipt records.
  Commit signatures bind signed content to a key under a trust policy;
  they do not independently prove its claimed creation time. A validated
  trusted timestamp can support existence by a time, not exact creation.

RISK TAXONOMY, NOT A VULNERABILITY ASSIGNMENT

- Numeric representation/truncation: CWE-197; wraparound: CWE-190.
- Validation: CWE-20 (broad category). Downstream trust: CWE-807 only
  where a security decision relies on untrusted input. Display confusion
  alone does not establish it. These labels do not establish severity.

REFERENCES AND EVIDENCE

Git date controls: https://git-scm.com/docs/git-commit#_commit_information
Archive behavior: https://git-scm.com/docs/git-archive#_description
CWE taxonomy: https://cwe.mitre.org/data/definitions/197.html
https://cwe.mitre.org/data/definitions/190.html
https://cwe.mitre.org/data/definitions/20.html
https://cwe.mitre.org/data/definitions/807.html
Trusted timestamp semantics: https://www.rfc-editor.org/rfc/rfc3161
Evidence: recorded 2026-10-01 consumer-date-impact results and Git matrix;
no new experiment is claimed by this guidance.

Matthew E. Luallen (@meluallen), with research, reproduction and drafting
assistance from OpenAI Codex.

```

## m@sph3r3.com, 2026-10-06 03:22

Subject: Re: [BUG] ZIP timestamp conversion and strict fast-import date validation
Message-ID: <CA+h9NxSzuNNQZ0pKKso9Y8BkXKZYgrGFLL050Pi3O7PC_gi65g@mail.gmail.com>
In-Reply-To: <f8dc40a4-920d-4dc5-9f71-7686bfc6255b@web.de>

```
Hi René,

Thanks for the explanation. Python 3.14.7’s ZipInfo.date_time is one
example: it reads the DOS date as 2100 for the 1972 archive, even
though the correct Unix timestamp is present. This affects metadata
reading; the tested UnZip and bsdtar restored 1972 correctly.

The 2106 case is separate: the Unix timestamp wraps to 1970, causing
UnZip update mode to retain different existing 2025 content. The 2038
control replaced that content as expected.

Best,
Matthew

On Mon, 05 Oct 2026 21:49:33 +0200, René Scharfe <l.s.r@web.de> wrote:
> On 10/5/26 3:48 PM, Matthew E. Luallen wrote:
> > Hello Git community,
> >
> > I'm reporting three date-handling bugs reproduced on Apple Git 2.50.1
> > and upstream Git 2.56.0:
> >
> > 1. Exporting a 1972-dated commit with git archive --format=zip produces
> >    a legacy DOS date interpreted as 2100, while the extended Unix
> >    timestamp retains 1972.
>
> DOS timestamps can express years starting from 1980.  Unix timestamps
> start at 1970.  We can't provide a DOS timestamp for 1972, but can we do
> better?  zip(1) clamps DOS timestamps to zero = the DOS epoch =
> 1980-01-01 00:00:00.
>
> Is anything using the DOS timestamp even though a Unix timestamp is
> present?
>
> > 2. Exporting a commit at epoch 4294967296 (2106-02-07 06:28:16 UTC)
> >    wraps ZIP's four-byte extended timestamp to zero (1970). Exporting
> >    the same commit as TAR preserves the original value.
>
> tar's timestamp can range from 1970 to 2242 with standard headers and
> beyond indefinitely with extended headers.
>
> We cannot put a higher value than 4294967295 into the four-byte field
> provided by the Unix time extension for ZIP, but we could clamp to that
> value.  zip(1) wraps around as well, though..
>
> (I'm using "Zip 3.0 (July 5th 2008), by Info-ZIP, with modifications by
> Apple Inc.")
>
> > 3. git fast-import --date-format=raw accepts -32184000 +0000, but
> >    git fsck --strict then reports badDate and ISO rendering returns
> >    literal placeholders. This occurs in strict raw mode, not just
> >    the deliberately permissive import mode.
>
> Well, raw mode passes on the timestamp value with only little checks.
> strtoul(3) used in builtin/fast-import.c::validate_raw_date() happily
> accepts negative numbers.  The latter function contains two NEEDSWORK
> comments about perhaps adding more checks, though.
>
> René


```
