Wrap unversioned CEL library initializer calls with guard

Kubernetes-commit: dc5e2f3fa295276029535359246154021861fdd6
This commit is contained in:
Joe Betz 2024-11-06 15:32:15 -05:00 committed by Kubernetes Publisher
parent ba14b9c42a
commit accfd98e20
2 changed files with 28 additions and 11 deletions

View File

@ -300,6 +300,6 @@ var hasPatchTypes = environment.VersionedOptions{
IntroducedVersion: version.MajorMinor(1, 0), IntroducedVersion: version.MajorMinor(1, 0),
EnvOptions: []cel.EnvOption{ EnvOptions: []cel.EnvOption{
common.ResolverEnvOption(&mutation.DynamicTypeResolver{}), common.ResolverEnvOption(&mutation.DynamicTypeResolver{}),
library.JSONPatch(), // for jsonPatch.escape() function environment.UnversionedLib(library.JSONPatch), // for jsonPatch.escape() function
}, },
} }

View File

@ -72,9 +72,9 @@ var baseOptsWithoutStrictCost = []VersionedOptions{
cel.EagerlyValidateDeclarations(true), cel.EagerlyValidateDeclarations(true),
cel.DefaultUTCTimeZone(true), cel.DefaultUTCTimeZone(true),
library.URLs(), UnversionedLib(library.URLs),
library.Regex(), UnversionedLib(library.Regex),
library.Lists(), UnversionedLib(library.Lists),
// cel-go v0.17.7 change the cost of has() from 0 to 1, but also provided the CostEstimatorOptions option to preserve the old behavior, so we enabled it at the same time we bumped our cel version to v0.17.7. // cel-go v0.17.7 change the cost of has() from 0 to 1, but also provided the CostEstimatorOptions option to preserve the old behavior, so we enabled it at the same time we bumped our cel version to v0.17.7.
// Since it is a regression fix, we apply it uniformly to all code use v0.17.7. // Since it is a regression fix, we apply it uniformly to all code use v0.17.7.
@ -92,7 +92,7 @@ var baseOptsWithoutStrictCost = []VersionedOptions{
{ {
IntroducedVersion: version.MajorMinor(1, 27), IntroducedVersion: version.MajorMinor(1, 27),
EnvOptions: []cel.EnvOption{ EnvOptions: []cel.EnvOption{
library.Authz(), UnversionedLib(library.Authz),
}, },
}, },
{ {
@ -100,7 +100,7 @@ var baseOptsWithoutStrictCost = []VersionedOptions{
EnvOptions: []cel.EnvOption{ EnvOptions: []cel.EnvOption{
cel.CrossTypeNumericComparisons(true), cel.CrossTypeNumericComparisons(true),
cel.OptionalTypes(), cel.OptionalTypes(),
library.Quantity(), UnversionedLib(library.Quantity),
}, },
}, },
// add the new validator in 1.29 // add the new validator in 1.29
@ -139,15 +139,15 @@ var baseOptsWithoutStrictCost = []VersionedOptions{
{ {
IntroducedVersion: version.MajorMinor(1, 30), IntroducedVersion: version.MajorMinor(1, 30),
EnvOptions: []cel.EnvOption{ EnvOptions: []cel.EnvOption{
library.IP(), UnversionedLib(library.IP),
library.CIDR(), UnversionedLib(library.CIDR),
}, },
}, },
// Format Library // Format Library
{ {
IntroducedVersion: version.MajorMinor(1, 31), IntroducedVersion: version.MajorMinor(1, 31),
EnvOptions: []cel.EnvOption{ EnvOptions: []cel.EnvOption{
library.Format(), UnversionedLib(library.Format),
}, },
}, },
// Authz selectors // Authz selectors
@ -166,14 +166,14 @@ var baseOptsWithoutStrictCost = []VersionedOptions{
return enabled return enabled
}, },
EnvOptions: []cel.EnvOption{ EnvOptions: []cel.EnvOption{
library.AuthzSelectors(), UnversionedLib(library.AuthzSelectors),
}, },
}, },
// Two variable comprehensions // Two variable comprehensions
{ {
IntroducedVersion: version.MajorMinor(1, 32), IntroducedVersion: version.MajorMinor(1, 32),
EnvOptions: []cel.EnvOption{ EnvOptions: []cel.EnvOption{
ext.TwoVarComprehensions(), UnversionedLib(ext.TwoVarComprehensions),
}, },
}, },
} }
@ -264,3 +264,20 @@ var (
baseEnvsSingleflight = &singleflight.Group{} baseEnvsSingleflight = &singleflight.Group{}
baseEnvsWithOptionSingleflight = &singleflight.Group{} baseEnvsWithOptionSingleflight = &singleflight.Group{}
) )
// UnversionedLib wraps library initialization calls like ext.Sets() or library.IP()
// to force compilation errors if the call evolves to include a varadic variable option.
//
// This provides automatic detection of a problem that is hard to catch in review--
// If a CEL library used in Kubernetes is unversioned and then become versioned, and we
// fail to set a desired version, the libraries defaults to the latest version, changing
// CEL environment without controlled rollout, bypassing the entire purpose of the base
// environment.
//
// If usages of this function fail to compile: add version=1 argument to all call sites
// that fail compilation while removing the UnversionedLib wrapper. Next, review
// the changes in the library present in higher versions and, if needed, use VersionedOptions to
// the base environment to roll out to a newer version safely.
func UnversionedLib(initializer func() cel.EnvOption) cel.EnvOption {
return initializer()
}