#1150291 apt: sqv method passes an invalid --policy-as-of date on 29 February

Package:
apt
Source:
apt
Description:
commandline package manager
Submitter:
Christian Noé Ramos López
Date:
2026-10-21 04:31:02 UTC
Severity:
normal
#1150291#5
Date:
2026-10-08 05:47:19 UTC
From:
To:
The sqv method checks each signature against next year's policy first
(methods/sqv.cc, VerifyGetSigners). It builds that date as

    strprintf(yearAheadDate, "%d-%d-%d", tm->tm_year + 1900 + 1, tm->tm_mon
+ 1, tm->tm_mday);

On 29 February this gives 29 February of a year that does not have one.
sqv refuses the argument:

    $ sqv ... --policy-as-of 2029-2-29 ...
    error: invalid value '2029-2-29' for '--policy-as-of <TIMESTAMP>':
Malformed timestamp: 2029-2-29.

and exits 2. The method treats any failure of the year-ahead run as the
policy rejecting the signature, runs sqv again at today's policy, and
warns. So on 29 February every repository gets

    W: .../InRelease: Policy will reject signature within a year, see
--audit for details

with the error above as its audit reason, and the year-ahead check does
not happen that day.

I reproduced it with apt-get update on a local file:// repository
signed by an ed25519 key, under a clock set to 2028-02-29 (an
LD_PRELOAD shim on clock_gettime/gettimeofday/time, inherited by the
method and sqv). With the clock at 2028-02-28 or 2028-03-01 there is no
warning, and with APT::Key::GPGVCommand set there is none on 29 February
either. The code is the same on main.

Two things would avoid it: computing the date with a calendar, or
passing "+1y", which sqv accepts as an offset (--policy-as-of +1y
exits 0 here). Separately: sqv's exit 2 is a usage error, and the
method reports it as a policy rejection.

#1150291#10
Date:
2026-10-08 07:14:23 UTC
From:
To:
Hi,

that is an interesting find!
(And some sad code)

Am Wed, Oct 07, 2026 at 11:47:19PM -0600, schrieb Christian Noé Ramos López:
with what is available via <chrono> in C++20 and above (nowadays).


The second option seems (at least for now) to be a usage error itself
as the documentation (sqv manpage) for --policy-as-of says
| The time is expressed as an ISO 8601 formatted timestamp.

And earlier it expressed in general:
| The TIMESTAMPs must be given in ISO 8601 format

I presume that +1y works stems from --time which says:
| Set the reference time as an ISO 8601 formatted timestamp or
| relative offset. […] This time is also used as the default
| value for --not-after and --policy-as-of […]

"relative offset" does not seem defined through.

Aside: I /suppose/ "PY1" would be a valid ISO 8601 (duration),
but I haven't dared to try how sqv would react.


Maybe its just me, but I would prefer the former (even if documentation
of sqv is changed) as the wording --policy-as-of highly suggests an
exact date for me… even if I realize as of 5 seconds later that this
might be a valid way of speaking, too.


Best regards

David Kalnischkies

#1150291#15
Date:
2026-10-12 19:15:23 UTC
From:
To:
Yes we actually want to use midnight here. I'm not sure if the best
approach is to the the 1 year from now or to truncate the day field
until it produces a valid date, such that 02-29 truncates to 02-28
rather than extends to 03-01 (semantically preserving the end of
month).

I guess it's easier though to do the chrono change and perhaps
that's what we should do yeah.

#1150291#20
Date:
2026-10-21 04:29:40 UTC
From:
To:
One thing that outlives whichever lands. In my rows sqv exits 1 when the
policy rejects a signature and 2 on a usage error, and the year-ahead
pass does not separate them -- both arrive as a policy rejection and warn
on every repository. The leap day is one way to reach exit 2 there; any
other usage-level failure in that pass would look the same. Is separating
them worth doing?

Christian Ramos
Norte Software
chris@nortesoftware.dev

El lun, 12 oct 2026 a la(s) 1:15 p.m., Julian Andres Klode
(jak@debian.org) escribió: