VYPR
Moderate severityNVD Advisory· Published Jan 31, 2024· Updated Nov 12, 2024

BuildKit possible panic when incorrect parameters sent from frontend

CVE-2024-23650

Description

BuildKit is a toolkit for converting source code to build artifacts in an efficient, expressive and repeatable manner. A malicious BuildKit client or frontend could craft a request that could lead to BuildKit daemon crashing with a panic. The issue has been fixed in v0.12.5. As a workaround, avoid using BuildKit frontends from untrusted sources.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

A malicious BuildKit client or frontend can craft a request causing the daemon to crash with a panic; fixed in v0.12.5.

Vulnerability

Description

CVE-2024-23650 is a denial-of-service vulnerability in BuildKit, a toolkit for converting source code into build artifacts. A malicious BuildKit client or frontend can send a crafted request that triggers a nil pointer dereference in the source policy validation logic, leading to a crash of the BuildKit daemon [1][2]. The root cause is insufficient validation of nil values in source policy rules, as addressed in commit e1924dc [3] and pull request #4601 [4].

Exploitation

The attack can be carried out by an unauthenticated attacker who is able to interact with the BuildKit daemon, either as a client or by providing a malicious frontend. No special privileges are required beyond the ability to send requests to the daemon. The crafted request exploits missing checks for nil selectors or actions within policy rules, causing the daemon to panic when processing the request [3][4].

Impact

Successful exploitation results in a crash of the BuildKit daemon, causing a denial of service. Build processes running on the affected daemon would be terminated, and any pending builds would be lost. The vulnerability does not allow arbitrary code execution or data exfiltration, but it can disrupt build pipelines and automated deployments.

Mitigation

The issue has been fixed in BuildKit version 0.12.5 [2]. Users should upgrade to this version or later. As a workaround, administrators should avoid using BuildKit frontends from untrusted sources and restrict network access to the BuildKit daemon to trusted clients only [1][2].

AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/moby/buildkitGo
< 0.12.50.12.5

Affected products

125

Patches

5
7718bd5c3dc8

pb: add extra validation to protobuf types

https://github.com/moby/buildkitTonis TiigiDec 18, 2023via ghsa
6 files changed · +64 12
  • client/validation_test.go+5 3 modified
    @@ -17,6 +17,7 @@ import (
     
     func testValidateNullConfig(t *testing.T, sb integration.Sandbox) {
     	requiresLinux(t)
    +	integration.CheckFeatureCompat(t, sb, integration.FeatureOCIExporter)
     
     	ctx := sb.Context()
     
    @@ -55,6 +56,7 @@ func testValidateNullConfig(t *testing.T, sb integration.Sandbox) {
     
     func testValidateInvalidConfig(t *testing.T, sb integration.Sandbox) {
     	requiresLinux(t)
    +	integration.CheckFeatureCompat(t, sb, integration.FeatureOCIExporter)
     
     	ctx := sb.Context()
     
    @@ -96,11 +98,12 @@ func testValidateInvalidConfig(t *testing.T, sb integration.Sandbox) {
     		},
     	}, "", b, nil)
     	require.Error(t, err)
    -	require.Contains(t, err.Error(), "invalid image config for export: missing os")
    +	require.Contains(t, err.Error(), "invalid image config: os and architecture must be specified together")
     }
     
     func testValidatePlatformsEmpty(t *testing.T, sb integration.Sandbox) {
     	requiresLinux(t)
    +	integration.CheckFeatureCompat(t, sb, integration.FeatureOCIExporter)
     
     	ctx := sb.Context()
     
    @@ -139,6 +142,7 @@ func testValidatePlatformsEmpty(t *testing.T, sb integration.Sandbox) {
     
     func testValidatePlatformsInvalid(t *testing.T, sb integration.Sandbox) {
     	requiresLinux(t)
    +	integration.CheckFeatureCompat(t, sb, integration.FeatureOCIExporter)
     
     	ctx := sb.Context()
     
    @@ -271,7 +275,6 @@ func testValidateSourcePolicy(t *testing.T, sb integration.Sandbox) {
     
     	for _, tc := range tcases {
     		t.Run(tc.name, func(t *testing.T) {
    -
     			var viaFrontend bool
     
     			b := func(ctx context.Context, c client.Client) (*client.Result, error) {
    @@ -302,7 +305,6 @@ func testValidateSourcePolicy(t *testing.T, sb integration.Sandbox) {
     			_, err = c.Build(ctx, SolveOpt{}, "", b, nil)
     			require.Error(t, err)
     			require.Contains(t, err.Error(), tc.exp)
    -
     		})
     	}
     }
    
  • control/control.go+3 0 modified
    @@ -405,6 +405,9 @@ func (c *Controller) Solve(ctx context.Context, req *controlapi.SolveRequest) (*
     
     	var cacheImports []frontend.CacheOptionsEntry
     	for _, im := range req.Cache.Imports {
    +		if im == nil {
    +			continue
    +		}
     		cacheImports = append(cacheImports, frontend.CacheOptionsEntry{
     			Type:  im.Type,
     			Attrs: im.Attrs,
    
  • frontend/gateway/client/attestation.go+6 0 modified
    @@ -30,8 +30,14 @@ func AttestationToPB[T any](a *result.Attestation[T]) (*pb.Attestation, error) {
     }
     
     func AttestationFromPB[T any](a *pb.Attestation) (*result.Attestation[T], error) {
    +	if a == nil {
    +		return nil, errors.Errorf("invalid nil attestation")
    +	}
     	subjects := make([]result.InTotoSubject, len(a.InTotoSubjects))
     	for i, subject := range a.InTotoSubjects {
    +		if subject == nil {
    +			return nil, errors.Errorf("invalid nil attestation subject")
    +		}
     		subjects[i] = result.InTotoSubject{
     			Kind:   subject.Kind,
     			Name:   subject.Name,
    
  • frontend/gateway/gateway.go+15 0 modified
    @@ -646,12 +646,21 @@ func (lbf *llbBridgeForwarder) registerResultIDs(results ...solver.Result) (ids
     func (lbf *llbBridgeForwarder) Solve(ctx context.Context, req *pb.SolveRequest) (*pb.SolveResponse, error) {
     	var cacheImports []frontend.CacheOptionsEntry
     	for _, e := range req.CacheImports {
    +		if e == nil {
    +			return nil, errors.Errorf("invalid nil cache import")
    +		}
     		cacheImports = append(cacheImports, frontend.CacheOptionsEntry{
     			Type:  e.Type,
     			Attrs: e.Attrs,
     		})
     	}
     
    +	for _, p := range req.SourcePolicies {
    +		if p == nil {
    +			return nil, errors.Errorf("invalid nil source policy")
    +		}
    +	}
    +
     	ctx = tracing.ContextWithSpanFromContext(ctx, lbf.callCtx)
     	res, err := lbf.llbBridge.Solve(ctx, frontend.SolveRequest{
     		Evaluate:       req.Evaluate,
    @@ -1077,6 +1086,12 @@ func (lbf *llbBridgeForwarder) ReleaseContainer(ctx context.Context, in *pb.Rele
     }
     
     func (lbf *llbBridgeForwarder) Warn(ctx context.Context, in *pb.WarnRequest) (*pb.WarnResponse, error) {
    +	// validate ranges are valid
    +	for _, r := range in.Ranges {
    +		if r == nil {
    +			return nil, status.Errorf(codes.InvalidArgument, "invalid source range")
    +		}
    +	}
     	err := lbf.llbBridge.Warn(ctx, in.Digest, string(in.Short), frontend.WarnOpts{
     		Level:      int(in.Level),
     		SourceInfo: in.Info,
    
  • util/tracing/transform/attribute.go+16 5 modified
    @@ -13,6 +13,9 @@ func Attributes(attrs []*commonpb.KeyValue) []attribute.KeyValue {
     
     	out := make([]attribute.KeyValue, 0, len(attrs))
     	for _, a := range attrs {
    +		if a == nil {
    +			continue
    +		}
     		kv := attribute.KeyValue{
     			Key:   attribute.Key(a.Key),
     			Value: toValue(a.Value),
    @@ -42,37 +45,45 @@ func toValue(v *commonpb.AnyValue) attribute.Value {
     func boolArray(kv []*commonpb.AnyValue) attribute.Value {
     	arr := make([]bool, len(kv))
     	for i, v := range kv {
    -		arr[i] = v.GetBoolValue()
    +		if v != nil {
    +			arr[i] = v.GetBoolValue()
    +		}
     	}
     	return attribute.BoolSliceValue(arr)
     }
     
     func intArray(kv []*commonpb.AnyValue) attribute.Value {
     	arr := make([]int64, len(kv))
     	for i, v := range kv {
    -		arr[i] = v.GetIntValue()
    +		if v != nil {
    +			arr[i] = v.GetIntValue()
    +		}
     	}
     	return attribute.Int64SliceValue(arr)
     }
     
     func doubleArray(kv []*commonpb.AnyValue) attribute.Value {
     	arr := make([]float64, len(kv))
     	for i, v := range kv {
    -		arr[i] = v.GetDoubleValue()
    +		if v != nil {
    +			arr[i] = v.GetDoubleValue()
    +		}
     	}
     	return attribute.Float64SliceValue(arr)
     }
     
     func stringArray(kv []*commonpb.AnyValue) attribute.Value {
     	arr := make([]string, len(kv))
     	for i, v := range kv {
    -		arr[i] = v.GetStringValue()
    +		if v != nil {
    +			arr[i] = v.GetStringValue()
    +		}
     	}
     	return attribute.StringSliceValue(arr)
     }
     
     func arrayValues(kv []*commonpb.AnyValue) attribute.Value {
    -	if len(kv) == 0 {
    +	if len(kv) == 0 || kv[0] == nil {
     		return attribute.StringSliceValue([]string{})
     	}
     
    
  • util/tracing/transform/span.go+19 4 modified
    @@ -32,14 +32,20 @@ func Spans(sdl []*tracepb.ResourceSpans) []tracesdk.ReadOnlySpan {
     		}
     
     		for _, sdi := range sd.ScopeSpans {
    -			sda := make([]tracesdk.ReadOnlySpan, len(sdi.Spans))
    -			for i, s := range sdi.Spans {
    -				sda[i] = &readOnlySpan{
    +			if sdi == nil {
    +				continue
    +			}
    +			sda := make([]tracesdk.ReadOnlySpan, 0, len(sdi.Spans))
    +			for _, s := range sdi.Spans {
    +				if s == nil {
    +					continue
    +				}
    +				sda = append(sda, &readOnlySpan{
     					pb:        s,
     					is:        sdi.Scope,
     					resource:  sd.Resource,
     					schemaURL: sd.SchemaUrl,
    -				}
    +				})
     			}
     			out = append(out, sda...)
     		}
    @@ -170,6 +176,9 @@ var _ tracesdk.ReadOnlySpan = &readOnlySpan{}
     
     // status transform a OTLP span status into span code.
     func statusCode(st *tracepb.Status) codes.Code {
    +	if st == nil {
    +		return codes.Unset
    +	}
     	switch st.Code {
     	case tracepb.Status_STATUS_CODE_ERROR:
     		return codes.Error
    @@ -186,6 +195,9 @@ func links(links []*tracepb.Span_Link) []tracesdk.Link {
     
     	sl := make([]tracesdk.Link, 0, len(links))
     	for _, otLink := range links {
    +		if otLink == nil {
    +			continue
    +		}
     		// This redefinition is necessary to prevent otLink.*ID[:] copies
     		// being reused -- in short we need a new otLink per iteration.
     		otLink := otLink
    @@ -226,6 +238,9 @@ func spanEvents(es []*tracepb.Span_Event) []tracesdk.Event {
     		if messageEvents >= maxMessageEventsPerSpan {
     			break
     		}
    +		if e == nil {
    +			continue
    +		}
     		messageEvents++
     		events = append(events,
     			tracesdk.Event{
    
e1924dc32da3

sourcepolicy: add validations for nil values

https://github.com/moby/buildkitTonis TiigiDec 18, 2023via ghsa
5 files changed · +137 0
  • client/client_test.go+1 0 modified
    @@ -210,6 +210,7 @@ func TestIntegration(t *testing.T) {
     		testValidateInvalidConfig,
     		testValidatePlatformsEmpty,
     		testValidatePlatformsInvalid,
    +		testValidateSourcePolicy,
     	)
     }
     
    
  • client/validation_test.go+102 0 modified
    @@ -9,6 +9,7 @@ import (
     	"github.com/moby/buildkit/client/llb"
     	"github.com/moby/buildkit/exporter/containerimage/exptypes"
     	"github.com/moby/buildkit/frontend/gateway/client"
    +	sppb "github.com/moby/buildkit/sourcepolicy/pb"
     	"github.com/moby/buildkit/util/testutil/integration"
     	ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
     	"github.com/stretchr/testify/require"
    @@ -204,3 +205,104 @@ func testValidatePlatformsInvalid(t *testing.T, sb integration.Sandbox) {
     		})
     	}
     }
    +
    +func testValidateSourcePolicy(t *testing.T, sb integration.Sandbox) {
    +	requiresLinux(t)
    +
    +	ctx := sb.Context()
    +
    +	c, err := New(ctx, sb.Address())
    +	require.NoError(t, err)
    +	defer c.Close()
    +
    +	tcases := []struct {
    +		name  string
    +		value *sppb.Policy
    +		exp   string
    +	}{
    +		// this condition fails on marshaling atm
    +		// {
    +		// 	name: "nilrule",
    +		// 	value: &sppb.Policy{
    +		// 		Rules: []*sppb.Rule{nil},
    +		// 	},
    +		// 	exp: "",
    +		// },
    +		{
    +			name: "nilselector",
    +			value: &sppb.Policy{
    +				Rules: []*sppb.Rule{
    +					{
    +						Action: sppb.PolicyAction_CONVERT,
    +					},
    +				},
    +			},
    +			exp: "invalid nil selector in policy",
    +		},
    +		{
    +			name: "emptyaction",
    +			value: &sppb.Policy{
    +				Rules: []*sppb.Rule{
    +					{
    +						Action: sppb.PolicyAction(9000),
    +						Selector: &sppb.Selector{
    +							Identifier: "docker-image://docker.io/library/alpine:latest",
    +						},
    +					},
    +				},
    +			},
    +			exp: "unknown type",
    +		},
    +		{
    +			name: "nilupdates",
    +			value: &sppb.Policy{
    +				Rules: []*sppb.Rule{
    +					{
    +						Action: sppb.PolicyAction_CONVERT,
    +						Selector: &sppb.Selector{
    +							Identifier: "docker-image://docker.io/library/alpine:latest",
    +						},
    +					},
    +				},
    +			},
    +			exp: "missing destination for convert rule",
    +		},
    +	}
    +
    +	for _, tc := range tcases {
    +		t.Run(tc.name, func(t *testing.T) {
    +
    +			var viaFrontend bool
    +
    +			b := func(ctx context.Context, c client.Client) (*client.Result, error) {
    +				def, err := llb.Image("alpine").Marshal(ctx)
    +				if err != nil {
    +					return nil, err
    +				}
    +
    +				req := client.SolveRequest{
    +					Evaluate:   true,
    +					Definition: def.ToPB(),
    +				}
    +				if viaFrontend {
    +					req.SourcePolicies = []*sppb.Policy{
    +						tc.value,
    +					}
    +				}
    +				return c.Solve(ctx, req)
    +			}
    +
    +			_, err = c.Build(ctx, SolveOpt{
    +				SourcePolicy: tc.value,
    +			}, "", b, nil)
    +			require.Error(t, err)
    +			require.Contains(t, err.Error(), tc.exp)
    +
    +			viaFrontend = true
    +			_, err = c.Build(ctx, SolveOpt{}, "", b, nil)
    +			require.Error(t, err)
    +			require.Contains(t, err.Error(), tc.exp)
    +
    +		})
    +	}
    +}
    
  • solver/llbsolver/bridge.go+8 0 modified
    @@ -79,6 +79,14 @@ func (b *llbBridge) loadResult(ctx context.Context, def *pb.Definition, cacheImp
     	}
     	var polEngine SourcePolicyEvaluator
     	if srcPol != nil || len(pol) > 0 {
    +		for _, p := range pol {
    +			if p == nil {
    +				return nil, errors.Errorf("invalid nil policy")
    +			}
    +			if err := validateSourcePolicy(*p); err != nil {
    +				return nil, err
    +			}
    +		}
     		if srcPol != nil {
     			pol = append([]*spb.Policy{srcPol}, pol...)
     		}
    
  • solver/llbsolver/solver.go+23 0 modified
    @@ -447,6 +447,9 @@ func (s *Solver) Solve(ctx context.Context, id string, sessionID string, req fro
     	j.SetValue(keyEntitlements, set)
     
     	if srcPol != nil {
    +		if err := validateSourcePolicy(*srcPol); err != nil {
    +			return nil, err
    +		}
     		j.SetValue(keySourcePolicy, *srcPol)
     	}
     
    @@ -595,6 +598,23 @@ func (s *Solver) Solve(ctx context.Context, id string, sessionID string, req fro
     	}, nil
     }
     
    +func validateSourcePolicy(pol spb.Policy) error {
    +	for _, r := range pol.Rules {
    +		if r == nil {
    +			return errors.New("invalid nil rule in policy")
    +		}
    +		if r.Selector == nil {
    +			return errors.New("invalid nil selector in policy")
    +		}
    +		for _, c := range r.Selector.Constraints {
    +			if c == nil {
    +				return errors.New("invalid nil constraint in policy")
    +			}
    +		}
    +	}
    +	return nil
    +}
    +
     func runCacheExporters(ctx context.Context, exporters []RemoteCacheExporter, j *solver.Job, cached *result.Result[solver.CachedResult], inp *result.Result[cache.ImmutableRef]) (map[string]string, error) {
     	eg, ctx := errgroup.WithContext(ctx)
     	g := session.NewGroup(j.SessionID)
    @@ -991,6 +1011,9 @@ func loadSourcePolicy(b solver.Builder) (*spb.Policy, error) {
     			return errors.Errorf("invalid source policy %T", v)
     		}
     		for _, f := range x.Rules {
    +			if f == nil {
    +				return errors.Errorf("invalid nil policy rule")
    +			}
     			r := *f
     			srcPol.Rules = append(srcPol.Rules, &r)
     		}
    
  • sourcepolicy/matcher.go+3 0 modified
    @@ -10,6 +10,9 @@ import (
     
     func match(ctx context.Context, src *selectorCache, ref string, attrs map[string]string) (bool, error) {
     	for _, c := range src.Constraints {
    +		if c == nil {
    +			return false, errors.Errorf("invalid nil constraint for %v", src)
    +		}
     		switch c.Condition {
     		case spb.AttrMatch_EQUAL:
     			if attrs[c.Key] != c.Value {
    
96663dd35bf3

exporter: add validation for platforms key value

https://github.com/moby/buildkitTonis TiigiDec 18, 2023via ghsa
3 files changed · +124 0
  • client/client_test.go+2 0 modified
    @@ -208,6 +208,8 @@ func TestIntegration(t *testing.T) {
     		testExportLocalNoPlatformSplitOverwrite,
     		testValidateNullConfig,
     		testValidateInvalidConfig,
    +		testValidatePlatformsEmpty,
    +		testValidatePlatformsInvalid,
     	)
     }
     
    
  • client/validation_test.go+108 0 modified
    @@ -7,6 +7,7 @@ import (
     	"testing"
     
     	"github.com/moby/buildkit/client/llb"
    +	"github.com/moby/buildkit/exporter/containerimage/exptypes"
     	"github.com/moby/buildkit/frontend/gateway/client"
     	"github.com/moby/buildkit/util/testutil/integration"
     	ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
    @@ -96,3 +97,110 @@ func testValidateInvalidConfig(t *testing.T, sb integration.Sandbox) {
     	require.Error(t, err)
     	require.Contains(t, err.Error(), "invalid image config for export: missing os")
     }
    +
    +func testValidatePlatformsEmpty(t *testing.T, sb integration.Sandbox) {
    +	requiresLinux(t)
    +
    +	ctx := sb.Context()
    +
    +	c, err := New(ctx, sb.Address())
    +	require.NoError(t, err)
    +	defer c.Close()
    +
    +	b := func(ctx context.Context, c client.Client) (*client.Result, error) {
    +		def, err := llb.Scratch().Marshal(ctx)
    +		if err != nil {
    +			return nil, err
    +		}
    +
    +		res, err := c.Solve(ctx, client.SolveRequest{
    +			Evaluate:   true,
    +			Definition: def.ToPB(),
    +		})
    +		if err != nil {
    +			return nil, err
    +		}
    +		res.AddMeta("refs.platforms", []byte("null"))
    +		return res, nil
    +	}
    +
    +	_, err = c.Build(ctx, SolveOpt{
    +		Exports: []ExportEntry{
    +			{
    +				Type:   ExporterOCI,
    +				Output: fixedWriteCloser(nopWriteCloser{io.Discard}),
    +			},
    +		},
    +	}, "", b, nil)
    +	require.Error(t, err)
    +	require.Contains(t, err.Error(), "invalid empty platforms index for exporter")
    +}
    +
    +func testValidatePlatformsInvalid(t *testing.T, sb integration.Sandbox) {
    +	requiresLinux(t)
    +
    +	ctx := sb.Context()
    +
    +	c, err := New(ctx, sb.Address())
    +	require.NoError(t, err)
    +	defer c.Close()
    +
    +	tcases := []struct {
    +		name  string
    +		value []exptypes.Platform
    +		exp   string
    +	}{
    +		{
    +			name:  "emptyID",
    +			value: []exptypes.Platform{{}},
    +			exp:   "invalid empty platform key for exporter",
    +		},
    +		{
    +			name: "missingOS",
    +			value: []exptypes.Platform{
    +				{
    +					ID: "foo",
    +				},
    +			},
    +			exp: "invalid platform value",
    +		},
    +	}
    +
    +	for _, tc := range tcases {
    +		t.Run(tc.name, func(t *testing.T) {
    +			b := func(ctx context.Context, c client.Client) (*client.Result, error) {
    +				def, err := llb.Scratch().Marshal(ctx)
    +				if err != nil {
    +					return nil, err
    +				}
    +
    +				res, err := c.Solve(ctx, client.SolveRequest{
    +					Evaluate:   true,
    +					Definition: def.ToPB(),
    +				})
    +				if err != nil {
    +					return nil, err
    +				}
    +
    +				dt, err := json.Marshal(exptypes.Platforms{Platforms: tc.value})
    +				if err != nil {
    +					return nil, err
    +				}
    +
    +				res.AddMeta("refs.platforms", dt)
    +				return res, nil
    +			}
    +
    +			_, err = c.Build(ctx, SolveOpt{
    +				Exports: []ExportEntry{
    +					{
    +						Type:   ExporterOCI,
    +						Output: fixedWriteCloser(nopWriteCloser{io.Discard}),
    +					},
    +				},
    +			}, "", b, nil)
    +			require.Error(t, err)
    +			require.Contains(t, err.Error(), tc.exp)
    +		})
    +	}
    +}
    
  • exporter/containerimage/exptypes/parse.go+14 0 modified
    @@ -17,6 +17,18 @@ func ParsePlatforms(meta map[string][]byte) (Platforms, error) {
     				return Platforms{}, errors.Wrapf(err, "failed to parse platforms passed to provenance processor")
     			}
     		}
    +		if len(ps.Platforms) == 0 {
    +			return Platforms{}, errors.Errorf("invalid empty platforms index for exporter")
    +		}
    +		for i, p := range ps.Platforms {
    +			if p.ID == "" {
    +				return Platforms{}, errors.Errorf("invalid empty platform key for exporter")
    +			}
    +			if p.Platform.OS == "" || p.Platform.Architecture == "" {
    +				return Platforms{}, errors.Errorf("invalid platform value %v for exporter", p.Platform)
    +			}
    +			ps.Platforms[i].Platform = platforms.Normalize(p.Platform)
    +		}
     		return ps, nil
     	}
     
    @@ -36,6 +48,8 @@ func ParsePlatforms(meta map[string][]byte) (Platforms, error) {
     				OSFeatures:   img.OSFeatures,
     				Variant:      img.Variant,
     			}
    +		} else if img.OS != "" || img.Architecture != "" {
    +			return Platforms{}, errors.Errorf("invalid image config: os and architecture must be specified together")
     		}
     	}
     	p = platforms.Normalize(p)
    
481d9c45f473

exporter: add validation for invalid platorm

https://github.com/moby/buildkitTonis TiigiDec 18, 2023via ghsa
3 files changed · +59 0
  • client/client_test.go+4 0 modified
    @@ -207,6 +207,7 @@ func TestIntegration(t *testing.T) {
     		testExportLocalNoPlatformSplit,
     		testExportLocalNoPlatformSplitOverwrite,
     		testValidateNullConfig,
    +		testValidateInvalidConfig,
     	)
     }
     
    @@ -8490,6 +8491,7 @@ cat <<EOF > $BUILDKIT_SCAN_DESTINATION/spdx.json
     EOF
     `
     		img.Config.Cmd = []string{"/bin/sh", "-c", cmd}
    +		img.Platform = p
     		config, err := json.Marshal(img)
     		if err != nil {
     			return nil, errors.Wrapf(err, "failed to marshal image config")
    @@ -8768,6 +8770,7 @@ cat <<EOF > $BUILDKIT_SCAN_DESTINATION/spdx.json
     EOF
     `
     		img.Config.Cmd = []string{"/bin/sh", "-c", cmd}
    +		img.Platform = p
     		config, err := json.Marshal(img)
     		if err != nil {
     			return nil, errors.Wrapf(err, "failed to marshal image config")
    @@ -8820,6 +8823,7 @@ EOF
     
     		var img ocispecs.Image
     		img.Config.Cmd = []string{"/bin/sh", "-c", "cat /greeting"}
    +		img.Platform = p
     		config, err := json.Marshal(img)
     		if err != nil {
     			return nil, errors.Wrapf(err, "failed to marshal image config")
    
  • client/validation_test.go+48 0 modified
    @@ -2,12 +2,14 @@ package client
     
     import (
     	"context"
    +	"encoding/json"
     	"io"
     	"testing"
     
     	"github.com/moby/buildkit/client/llb"
     	"github.com/moby/buildkit/frontend/gateway/client"
     	"github.com/moby/buildkit/util/testutil/integration"
    +	ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
     	"github.com/stretchr/testify/require"
     )
     
    @@ -48,3 +50,49 @@ func testValidateNullConfig(t *testing.T, sb integration.Sandbox) {
     	require.Error(t, err)
     	require.Contains(t, err.Error(), "invalid null image config for export")
     }
    +
    +func testValidateInvalidConfig(t *testing.T, sb integration.Sandbox) {
    +	requiresLinux(t)
    +
    +	ctx := sb.Context()
    +
    +	c, err := New(ctx, sb.Address())
    +	require.NoError(t, err)
    +	defer c.Close()
    +
    +	b := func(ctx context.Context, c client.Client) (*client.Result, error) {
    +		def, err := llb.Scratch().Marshal(ctx)
    +		if err != nil {
    +			return nil, err
    +		}
    +
    +		res, err := c.Solve(ctx, client.SolveRequest{
    +			Evaluate:   true,
    +			Definition: def.ToPB(),
    +		})
    +		if err != nil {
    +			return nil, err
    +		}
    +		var img ocispecs.Image
    +		img.Platform = ocispecs.Platform{
    +			Architecture: "amd64",
    +		}
    +		dt, err := json.Marshal(img)
    +		if err != nil {
    +			return nil, err
    +		}
    +		res.AddMeta("containerimage.config", dt)
    +		return res, nil
    +	}
    +
    +	_, err = c.Build(ctx, SolveOpt{
    +		Exports: []ExportEntry{
    +			{
    +				Type:   ExporterOCI,
    +				Output: fixedWriteCloser(nopWriteCloser{io.Discard}),
    +			},
    +		},
    +	}, "", b, nil)
    +	require.Error(t, err)
    +	require.Contains(t, err.Error(), "invalid image config for export: missing os")
    +}
    
  • exporter/containerimage/writer.go+7 0 modified
    @@ -583,6 +583,13 @@ func patchImageConfig(dt []byte, descs []ocispecs.Descriptor, history []ocispecs
     		return nil, errors.Errorf("invalid null image config for export")
     	}
     
    +	if img.OS == "" {
    +		return nil, errors.Errorf("invalid image config for export: missing os")
    +	}
    +	if img.Architecture == "" {
    +		return nil, errors.Errorf("invalid image config for export: missing architecture")
    +	}
    +
     	var rootFS ocispecs.RootFS
     	rootFS.Type = "layers"
     	for _, desc := range descs {
    
83edaef59d54

exporter: validate null config metadata from gateway

https://github.com/moby/buildkitTonis TiigiDec 18, 2023via ghsa
3 files changed · +60 0
  • client/client_test.go+1 0 modified
    @@ -206,6 +206,7 @@ func TestIntegration(t *testing.T) {
     		testMultipleRecordsWithSameLayersCacheImportExport,
     		testExportLocalNoPlatformSplit,
     		testExportLocalNoPlatformSplitOverwrite,
    +		testValidateNullConfig,
     	)
     }
     
    
  • client/validation_test.go+50 0 added
    @@ -0,0 +1,50 @@
    +package client
    +
    +import (
    +	"context"
    +	"io"
    +	"testing"
    +
    +	"github.com/moby/buildkit/client/llb"
    +	"github.com/moby/buildkit/frontend/gateway/client"
    +	"github.com/moby/buildkit/util/testutil/integration"
    +	"github.com/stretchr/testify/require"
    +)
    +
    +func testValidateNullConfig(t *testing.T, sb integration.Sandbox) {
    +	requiresLinux(t)
    +
    +	ctx := sb.Context()
    +
    +	c, err := New(ctx, sb.Address())
    +	require.NoError(t, err)
    +	defer c.Close()
    +
    +	b := func(ctx context.Context, c client.Client) (*client.Result, error) {
    +		def, err := llb.Scratch().Marshal(ctx)
    +		if err != nil {
    +			return nil, err
    +		}
    +
    +		res, err := c.Solve(ctx, client.SolveRequest{
    +			Evaluate:   true,
    +			Definition: def.ToPB(),
    +		})
    +		if err != nil {
    +			return nil, err
    +		}
    +		res.AddMeta("containerimage.config", []byte("null"))
    +		return res, nil
    +	}
    +
    +	_, err = c.Build(ctx, SolveOpt{
    +		Exports: []ExportEntry{
    +			{
    +				Type:   ExporterOCI,
    +				Output: fixedWriteCloser(nopWriteCloser{io.Discard}),
    +			},
    +		},
    +	}, "", b, nil)
    +	require.Error(t, err)
    +	require.Contains(t, err.Error(), "invalid null image config for export")
    +}
    
  • exporter/containerimage/writer.go+9 0 modified
    @@ -569,11 +569,20 @@ func parseHistoryFromConfig(dt []byte) ([]ocispecs.History, error) {
     }
     
     func patchImageConfig(dt []byte, descs []ocispecs.Descriptor, history []ocispecs.History, cache []byte, epoch *time.Time) ([]byte, error) {
    +	var img ocispecs.Image
    +	if err := json.Unmarshal(dt, &img); err != nil {
    +		return nil, errors.Wrap(err, "invalid image config for export")
    +	}
    +
     	m := map[string]json.RawMessage{}
     	if err := json.Unmarshal(dt, &m); err != nil {
     		return nil, errors.Wrap(err, "failed to parse image config for patch")
     	}
     
    +	if m == nil {
    +		return nil, errors.Errorf("invalid null image config for export")
    +	}
    +
     	var rootFS ocispecs.RootFS
     	rootFS.Type = "layers"
     	for _, desc := range descs {
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

10

News mentions

0

No linked articles in our index yet.