Skip to content

Commit 4ea792f

Browse files
committed
fix: build profile with version
extractTalosSPDX was building the imager profile via ParseFromPath alone, leaving prof.Version empty. The asset cache keys on factoryprofile.Hash(prof), so every Talos version for a given (arch, output kind) collapsed to the same hash and reused whichever initramfs was built first. Two different versions therefore returned SPDX bundles extracted from identical initramfs payloads - only DocumentName and DocumentNamespace differed between responses. Signed-off-by: Mateusz Urbanek <mateusz.urbanek@siderolabs.com>
1 parent fcf9d57 commit 4ea792f

3 files changed

Lines changed: 80 additions & 4 deletions

File tree

enterprise/spdx/builder/builder.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"github.com/siderolabs/image-factory/internal/asset"
3131
"github.com/siderolabs/image-factory/internal/profile"
3232
"github.com/siderolabs/image-factory/internal/schematic"
33+
ifconstants "github.com/siderolabs/image-factory/pkg/constants"
3334
schematicpkg "github.com/siderolabs/image-factory/pkg/schematic"
3435
)
3536

@@ -143,10 +144,7 @@ func (b *Builder) buildBundle(sc *schematicpkg.Schematic, schematicID, versionTa
143144
Files: []File{},
144145
}
145146

146-
logger.Debug("extracting SPDX from Talos",
147-
zap.String("schematic", schematicID),
148-
zap.String("version", versionTag),
149-
zap.String("arch", string(arch)))
147+
logger.Debug("extracting SPDX from Talos")
150148

151149
// Extract SPDX from Talos
152150
var err error
@@ -195,6 +193,14 @@ func (b *Builder) extractTalosSPDX(ctx context.Context, bundle *Bundle, versionT
195193
return fmt.Errorf("invalid version: %w", err)
196194
}
197195

196+
// We are manually setting the profile version and name here because after enhancing the profile
197+
// the produced initramfs' magic number does not match what's expected by the decompression
198+
// library, causing it to fail to decompress and preventing us from extracting the embedded SPDX files.
199+
// That's why we are getting SBOM from each extension individually instead of relying on the
200+
// one embedded in the initramfs.
201+
prof.Version = talosVersion.String()
202+
prof.Name = ifconstants.TalosName
203+
198204
asset, err := b.assetBuilder.Build(ctx, prof, talosVersion.String(), path, path)
199205
if err != nil {
200206
return err

hack/dev/config.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ authentication:
3636
enterprise:
3737
vex:
3838
data:
39+
registry: registry.ghcr.io:5000
3940
namespace: siderolabs/image-factory
4041
repository: test-vex-data
42+
insecure: true
4143
cacheTTL: 15s

internal/integration/spdx_test.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,15 @@ import (
2424

2525
const spdxTestTalosVersion = "v1.12.4"
2626

27+
// Two patch releases used to assert that the SPDX bundle reflects the
28+
// requested Talos version. A previous bug cached the initramfs by a profile
29+
// hash that did not include the version, so different versions returned the
30+
// same package set.
31+
const (
32+
spdxRegressionVersionA = "v1.13.0"
33+
spdxRegressionVersionB = "v1.13.2"
34+
)
35+
2736
func downloadSPDX(ctx context.Context, t *testing.T, baseURL, schematicID, version, arch, method string) *http.Response {
2837
t.Helper()
2938

@@ -158,6 +167,65 @@ func testSPDXFrontend(ctx context.Context, t *testing.T, baseURL string) {
158167
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
159168
})
160169

170+
t.Run("differs across talos versions", func(t *testing.T) {
171+
t.Parallel()
172+
173+
fetchPackages := func(version string) []*spdx.Package {
174+
resp := downloadSPDX(ctx, t, baseURL, emptySchematicID, version, "amd64", http.MethodGet)
175+
require.Equal(t, http.StatusOK, resp.StatusCode)
176+
177+
doc, err := spdxjson.Read(resp.Body)
178+
require.NoError(t, err)
179+
180+
return doc.Packages
181+
}
182+
183+
pkgsA := fetchPackages(spdxRegressionVersionA)
184+
pkgsB := fetchPackages(spdxRegressionVersionB)
185+
186+
require.NotEmpty(t, pkgsA, "no packages extracted for %s", spdxRegressionVersionA)
187+
require.NotEmpty(t, pkgsB, "no packages extracted for %s", spdxRegressionVersionB)
188+
189+
type pkgKey struct {
190+
name string
191+
version string
192+
}
193+
194+
toSet := func(pkgs []*spdx.Package) map[pkgKey]struct{} {
195+
out := make(map[pkgKey]struct{}, len(pkgs))
196+
for _, p := range pkgs {
197+
out[pkgKey{name: p.PackageName, version: p.PackageVersion}] = struct{}{}
198+
}
199+
200+
return out
201+
}
202+
203+
setA := toSet(pkgsA)
204+
setB := toSet(pkgsB)
205+
206+
diff := 0
207+
208+
for k := range setA {
209+
if _, ok := setB[k]; !ok {
210+
diff++
211+
}
212+
}
213+
214+
for k := range setB {
215+
if _, ok := setA[k]; !ok {
216+
diff++
217+
}
218+
}
219+
220+
// Regression guard: the prior bug caused both versions to extract SPDX
221+
// from the same cached initramfs, so the (name, version) sets were
222+
// identical (diff == 0). Two adjacent patch releases must yield several
223+
// differing entries (kernel/userspace package bumps).
224+
assert.Greater(t, diff, 3,
225+
"SPDX package set differs across Talos versions %s vs %s by %d (name, version) entries; expected > 3 (was 0 under the cache-collision bug)",
226+
spdxRegressionVersionA, spdxRegressionVersionB, diff)
227+
})
228+
161229
t.Run("cached response", func(t *testing.T) {
162230
t.Parallel()
163231

0 commit comments

Comments
 (0)