Repository navigation
fix(chart): normalize StampModTimes timestamp to UTC/truncate + Chart.lock reproducibility test - #9
Conversation
….lock reproducibility test Source PR: helm#32485 Source head: e923167
✅ Shipwright · ApproveRecommendation: approve PR #9 · Tier
Findings (3)
Fireworks usage: 8,056 input · 440 output · 8,496 total tokens · $0.0021 · 9s · 0 fix iteration(s) Open the Shipwright check for full evidence and the audit bundle. Use |
| if hdr.Name != wantPath { | ||
| continue | ||
| } | ||
| found = true |
There was a problem hiding this comment.
Shipwright · HIGH
The test asserts the generated: field via require.Contains(t, string(raw), wantGenerated) where wantGenerated is epoch.Format(time.RFC3339).
Impact: The test asserts the generated: field via require.Contains(t, string(raw), wantGenerated) where wantGenerated is epoch.Format(time.RFC3339). This is a substring match against the entire marshaled YAML. If the lock file contains another field (e.g., a dependency's generated timestamp or a comment) that happens to contain the same RFC3339 string, the test could pass even when Lock.Generated is not actually stamped. A…
Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.
| // non-reproducible across machines even when the same SOURCE_DATE_EPOCH is used. | ||
| func (ch *Chart) StampModTimes(t time.Time) { | ||
| t = t.UTC().Truncate(time.Second) | ||
| ch.ModTime = t |
There was a problem hiding this comment.
Shipwright · HIGH
StampModTimes now mutates the caller's time.Time via t.UTC().Truncate() before assigning to ch.ModTime and Lock.Generated.
Impact: StampModTimes now mutates the caller's time.Time via t.UTC().Truncate() before assigning to ch.ModTime and Lock.Generated. If the same *time.Time pointer is shared across multiple charts or reused after this call, the caller's original value is silently altered to UTC/truncated, which could break subsequent logic that expects the original local-zone or sub-second value. The test itself works around this by pre-compu…
Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.
| @@ -0,0 +1,3 @@ | |||
| dependencies: [] | |||
| digest: sha256:0000000000000000000000000000000000000000000000000000000000000 | |||
There was a problem hiding this comment.
Shipwright · LOW
The new test fixture chart-with-lock/Chart.lock contains a hardcoded digest of all zeros (sha256:0000...).
Impact: The new test fixture chart-with-lock/Chart.lock contains a hardcoded digest of all zeros (sha256:0000...). While this is test data and not a real secret, it establishes a pattern of placeholder digests that could mask a future regression where digest computation is accidentally stubbed or skipped in production code paths. Consider using a realistic digest or documenting why the zero digest is safe for this test.
Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.
Closes helm#32396
What this PR does
Chart.StampModTimes(added in helm#32162) assigns the epoch time as-is. This PR adds at.UTC().Truncate(time.Second)normalization step before stamping.Why it matters
The two normalizations serve distinct purposes:
.UTC()is the actual reproducibility fix: atime.Timewith a non-UTC location produces agenerated:value with a timezone offset (e.g."2023-11-14T22:13:20+03:00") that varies across machines even when the same epoch value is passed..Truncate(time.Second)enforcesSOURCE_DATE_EPOCHsemantics: the spec defines the epoch as an integer number of seconds, so an SDK caller passing a sub-secondtime.Timewould produce agenerated:timestamp that does not match that integer. It also keeps the YAMLgenerated:value and the tar entry modtime aligned, since tar headers are second-granularity.Example without this fix:
# generated: "2023-11-14T22:13:20+03:00" <- timezone-dependent, non-reproducibleWith this fix:
# generated: "2023-11-14T19:13:20Z" <- always UTC, always reproducibleChanges
pkg/chart/v2/chart.go- normalizetbefore stamping:pkg/action/testdata/charts/chart-with-lock/- new test fixture: a minimal chart with aChart.lockwhosegenerated:predates anySOURCE_DATE_EPOCH.pkg/action/package_test.go-TestRunWithSourceDateEpochStampsLockGenerated: packages the fixture withSourceDateEpochset to a non-UTC, sub-secondtime.Timeand asserts:Chart.locktar entry modtime equals the normalized epoch.generated:field in the YAML body equals the normalized UTC timestamp.Notes
This is the follow-up scoped to exactly the two items from helm#32396. Everything else from the original helm#32173 is already present in
mainvia helm#32162.Source merge-base:
7e641d30a9355b43a9729d04c2771215ed926899Source head:
e92316751375bafd4621232bb39a1238a22e91c4