Skip to content

Commit 4a2659d

Browse files
committed
feat(ISV-6756): log a warning when missing build_finished_on timestamp
Signed-off-by: Martin Jediny <jedinym@proton.me>
1 parent 75f3492 commit 4a2659d

3 files changed

Lines changed: 22 additions & 17 deletions

File tree

src/mobster/oci/artifact.py

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class SLSAProvenance:
3434
"""
3535

3636
def __init__(
37-
self, build_finished_on: datetime.datetime, sbom_digests: dict[str, str]
37+
self, build_finished_on: datetime.datetime | None, sbom_digests: dict[str, str]
3838
) -> None:
3939
self._build_finished_on = build_finished_on
4040
self._sbom_digests: dict[str, str] = sbom_digests
@@ -96,9 +96,7 @@ def _parse_v02(predicate: Any) -> "SLSAProvenance":
9696
if finished_on:
9797
build_finished_on = dateutil.parser.isoparse(finished_on)
9898
else:
99-
build_finished_on = datetime.datetime.min.replace(
100-
tzinfo=datetime.timezone.utc
101-
)
99+
build_finished_on = None
102100

103101
# map image digests to sbom blob digests
104102
sbom_blob_urls: dict[str, str] = {}
@@ -139,9 +137,7 @@ def _parse_v1(predicate: Any) -> "SLSAProvenance":
139137
if finished_on:
140138
build_finished_on = dateutil.parser.isoparse(finished_on)
141139
else:
142-
build_finished_on = datetime.datetime.min.replace(
143-
tzinfo=datetime.timezone.utc
144-
)
140+
build_finished_on = None
145141

146142
image_digests: dict[str, str] = {}
147143
sbom_digests: dict[str, str] = {}
@@ -188,13 +184,13 @@ def _parse_v1(predicate: Any) -> "SLSAProvenance":
188184
return SLSAProvenance(build_finished_on, sbom_blob_urls)
189185

190186
@property
191-
def build_finished_on(self) -> datetime.datetime:
187+
def build_finished_on(self) -> datetime.datetime | None:
192188
"""
193189
Get the timestamp when the build finished.
194190
195191
Returns:
196-
The build completion timestamp, or datetime.min with UTC timezone
197-
if the timestamp was not available in the provenance data.
192+
The build completion timestamp, or None if the timestamp was not
193+
available in the provenance data.
198194
"""
199195
return self._build_finished_on
200196

src/mobster/oci/cosign/static.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import os
66
import tempfile
77
import typing
8+
from datetime import datetime, timezone
89
from pathlib import Path
910
from typing import Any, Literal
1011

@@ -127,7 +128,19 @@ async def fetch_latest_provenance(self, image: Image) -> SLSAProvenance:
127128
if len(provenances) == 0:
128129
raise SBOMError(f"No provenances parsed for image {image}.")
129130

130-
return sorted(provenances, key=lambda x: x.build_finished_on, reverse=True)[0]
131+
# sort the provenances to ensure we attempt to use the latest one
132+
if any(p.build_finished_on is None for p in provenances):
133+
logger.warning(
134+
"Some fetched provenances are missing build_finished_on information. "
135+
"datetime.min will be used as fallback for sorting."
136+
)
137+
138+
def key_by_build_finished_on(prov: SLSAProvenance) -> datetime:
139+
if prov.build_finished_on is not None:
140+
return prov.build_finished_on
141+
return datetime.min.replace(tzinfo=timezone.utc)
142+
143+
return sorted(provenances, key=key_by_build_finished_on, reverse=True)[0]
131144

132145
async def fetch_attested_sbom(
133146
self, image: Image, sbom_format: SBOMFormat

tests/oci/test_oci.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -449,9 +449,7 @@ def test_v02_missing_build_finished_on(self) -> None:
449449

450450
prov = SLSAProvenance.parse(raw)
451451

452-
assert prov.build_finished_on == datetime.datetime.min.replace(
453-
tzinfo=datetime.timezone.utc
454-
)
452+
assert prov.build_finished_on is None
455453

456454
def test_v02_task_missing_sbom_blob_url(self) -> None:
457455
raw = _make_slsa_raw(
@@ -531,9 +529,7 @@ def test_v1_missing_finished_on(self) -> None:
531529

532530
prov = SLSAProvenance.parse(raw)
533531

534-
assert prov.build_finished_on == datetime.datetime.min.replace(
535-
tzinfo=datetime.timezone.utc
536-
)
532+
assert prov.build_finished_on is None
537533

538534
def test_v1_bad_base64_in_byproduct(self) -> None:
539535
raw = _make_slsa_raw(

0 commit comments

Comments
 (0)