Git development
 help / color / mirror / Atom feed
* [BUG] ZIP timestamp conversion and strict fast-import date validation
@ 2026-10-05 13:48 Matthew E. Luallen
  2026-10-05 19:49 ` René Scharfe
  0 siblings, 1 reply; 5+ messages in thread
From: Matthew E. Luallen @ 2026-10-05 13:48 UTC (permalink / raw)
  To: git

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.

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [BUG] ZIP timestamp conversion and strict fast-import date validation
  2026-10-05 13:48 [BUG] ZIP timestamp conversion and strict fast-import date validation Matthew E. Luallen
@ 2026-10-05 19:49 ` René Scharfe
  2026-10-05 20:32   ` Matthew E. Luallen
  2026-10-06  3:22   ` m
  0 siblings, 2 replies; 5+ messages in thread
From: René Scharfe @ 2026-10-05 19:49 UTC (permalink / raw)
  To: Matthew E. Luallen, git

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é


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [BUG] ZIP timestamp conversion and strict fast-import date validation
  2026-10-05 19:49 ` René Scharfe
@ 2026-10-05 20:32   ` Matthew E. Luallen
  2026-10-07 15:33     ` René Scharfe
  2026-10-06  3:22   ` m
  1 sibling, 1 reply; 5+ messages in thread
From: Matthew E. Luallen @ 2026-10-05 20:32 UTC (permalink / raw)
  To: René Scharfe; +Cc: git

[-- Attachment #1: Type: text/plain, Size: 943 bytes --]

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

[-- Attachment #2: git-timestamp-guidance.txt --]
[-- Type: text/plain, Size: 3504 bytes --]

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.

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [BUG] ZIP timestamp conversion and strict fast-import date validation
  2026-10-05 19:49 ` René Scharfe
  2026-10-05 20:32   ` Matthew E. Luallen
@ 2026-10-06  3:22   ` m
  1 sibling, 0 replies; 5+ messages in thread
From: m @ 2026-10-06  3:22 UTC (permalink / raw)
  To: René Scharfe; +Cc: git

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é

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [BUG] ZIP timestamp conversion and strict fast-import date validation
  2026-10-05 20:32   ` Matthew E. Luallen
@ 2026-10-07 15:33     ` René Scharfe
  0 siblings, 0 replies; 5+ messages in thread
From: René Scharfe @ 2026-10-07 15:33 UTC (permalink / raw)
  To: Matthew E. Luallen; +Cc: git

On 10/5/26 10:32 PM, Matthew E. Luallen wrote:
> 
> - Reader example: Python 3.14.7's ZipInfo.date_time reports 2100 for
>   the 1972 ZIP despite its correct Unix timestamp.

Makes sense.  Its source code,
https://github.com/python/cpython/blob/3.14/Lib/zipfile/__init__.py,
contains a decode function for two extra fields, Zip64 (0x0001) and
Info-ZIP Unicode Path (0x7075), but no support for UNIX (0x000d).
And why should it?

> - 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.

I'm not aware of a ZIP extension that allows storing arbitrary dates.
I see three options:

- Don't color outside the lines, only emit ZIP files with valid DOS
  and UNIX timestamps and refuse to write earlier or later ones,

- wrap consistently, which can be confusing, but allows users to
  recover the original timestamp when they supply the higher bits
  (like we can say twenties now to mean 202x and 30 years ago we
  meant 192x), or

- map all earlier timestamps to the epoch and later ones to the maximum
  value, effectively stopping time at the borders -- all mtimes will be
  0xffffffff forever after that point is crossed, making them useless,
  except as a marker that a yet to be invented extension needs to be
  consulted to get the real mtime value.

> - 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?

Rejecting non-representable timestamps when creating ZIP files seems
like the most honest option.  Perhaps it's annoying enough to motivate
people to find a proper solution?  Which could be "use the tar format".

It would be nice if there was an example to follow.  Info-ZIP zip(1)
clamping at the low end and rolling over at the high end seems odd,
though.

Rejecting negative timestamps on fast-import or at all seems bad for
people who want to import ancient records.  Git commit objects store
timestamps as decimal numeric strings, so they can support arbitrarily
high and low values.  Importing mainframe file versions from the
sixties or versions of legal documents from the last few hundred years
don't seem too outlandish.

timestamp_t, Git's in-memory representation, is unsigned for
historical reasons, but that is not necessarily fixed in stone.

René


^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-10-07 15:57 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-05 13:48 [BUG] ZIP timestamp conversion and strict fast-import date validation Matthew E. Luallen
2026-10-05 19:49 ` René Scharfe
2026-10-05 20:32   ` Matthew E. Luallen
2026-10-07 15:33     ` René Scharfe
2026-10-06  3:22   ` m

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox