The judgment call this function makes (IA-5's main statement itself only says "change or
refresh authenticators [by an organization-defined period] or when [organization-defined events]
occur" -- it doesn't define how a pre-existing expiresAt and a new rotationPeriod/
lastRotatedAt pair should interact when both are declared on the same variable):
hasRotationData() false) -> "undeclared". Not an error --
most variables aren't authenticators, and saying nothing about rotation is the common, correct
case, not a violation to report.expiresAt is treated as authoritative and checked first, independent of everything else: if
it's a valid date already in the past, the status is "expired", full stop. Rationale: expiresAt
is a pre-existing, often externally-imposed hard deadline (an API key's own issuer-enforced
expiry, a certificate's NotAfter) -- once that date passes, the value has literally stopped
being valid, which is categorically worse than merely being overdue for an internal policy
rotation, and no internal rotation record can retroactively un-expire it.rotationPeriod is declared, it's checked against lastRotatedAt: both present,
both parseable (see parseRotationPeriodDays()), and lastRotatedAt no later than now ->
compute lastRotatedAt + rotationPeriod and compare to now ("overdue" if that due date has
passed, else "compliant"). Any other case with rotationPeriod declared -- lastRotatedAt
missing, either value not statically parseable, or lastRotatedAt itself in the future (a
rotation can't have happened yet, so trusting it would let a bad timestamp manufacture false
compliance) -- fails closed to "overdue": IA-5 asks for evidence a rotation actually happened
on schedule, and a declared obligation that can't be shown to have been met is reported as
non-compliant rather than silently passed as compliant.authenticatorType and/or lastRotatedAt and/or
rotationTriggerEvents -- but no rotationPeriod and no past-due expiresAt) -> "compliant":
nothing here states a time-based obligation this variable could be failing to meet.rotationTriggerEvents (the event-based trigger) deliberately never changes the returned status
by itself: env-cap has no way to observe whether a listed event (a suspected compromise, a
personnel change, ...) actually occurred, so treating its mere presence as either compliant or
overdue would be fabricating a signal. It's presence-only evidence that the event-based half of
IA-5's two-trigger model was at least declared -- a generator can and should still surface it,
just never fold it into this computed status.
Computes one variable's NIST SP 800-53 IA-5 rotation-compliance status from its lifecycle facts, relative to
now.