@@ -212,18 +212,100 @@ func newAWSProvider(spec *api.ProviderConfig, configurationLoader AWSConfigurati
212212 return provider , nil
213213}
214214
215+ // resolveYAMLAnchors processes YAML anchors and aliases, returning clean YAML
216+ // that can be safely parsed by strict unmarshaling. It removes the "aliases"
217+ // field commonly used for anchor definitions as it's not part of ClusterConfig schema.
218+ func resolveYAMLAnchors (data []byte ) ([]byte , error ) {
219+ // Security: Limit input size to prevent memory exhaustion attacks
220+ const maxInputSize = 1024 * 1024 // 1MB limit
221+ if len (data ) > maxInputSize {
222+ return nil , fmt .Errorf ("YAML input too large: %d bytes exceeds limit of %d bytes" , len (data ), maxInputSize )
223+ }
224+
225+ // Security: Check for excessive nesting depth to prevent stack overflow
226+ const maxNestingDepth = 10
227+ if nestingDepth := countNestingDepth (data ); nestingDepth > maxNestingDepth {
228+ return nil , fmt .Errorf ("YAML nesting too deep: %d levels exceeds limit of %d" , nestingDepth , maxNestingDepth )
229+ }
230+
231+ // Resolve YAML anchors and aliases by unmarshaling to interface{} first.
232+ // This step processes any YAML anchors (&anchor) and aliases (*alias) in the input,
233+ // expanding them to their full values.
234+ var resolved interface {}
235+ if err := yaml .Unmarshal (data , & resolved ); err != nil {
236+ return nil , err
237+ }
238+
239+ // Marshal back to get resolved YAML without anchors/aliases
240+ resolvedData , err := yaml .Marshal (resolved )
241+ if err != nil {
242+ return nil , err
243+ }
244+
245+ // Security: Check for excessive expansion (YAML bomb protection)
246+ const maxExpansionRatio = 10
247+ if len (resolvedData ) > len (data )* maxExpansionRatio {
248+ return nil , fmt .Errorf ("YAML expansion too large: %d bytes expanded from %d bytes (ratio: %d, limit: %d)" ,
249+ len (resolvedData ), len (data ), len (resolvedData )/ len (data ), maxExpansionRatio )
250+ }
251+
252+ // Remove the "aliases" field commonly used for YAML anchor definitions
253+ // as it's not part of the ClusterConfig schema
254+ var temp map [string ]interface {}
255+ if err := yaml .Unmarshal (resolvedData , & temp ); err != nil {
256+ return nil , err
257+ }
258+
259+ // Remove only the aliases field, maintaining strict validation for everything else
260+ delete (temp , "aliases" )
261+
262+ // Marshal back to clean YAML
263+ return yaml .Marshal (temp )
264+ }
265+
266+ // countNestingDepth estimates YAML nesting depth by counting indentation
267+ func countNestingDepth (data []byte ) int {
268+ lines := strings .Split (string (data ), "\n " )
269+ maxDepth := 0
270+ for _ , line := range lines {
271+ if strings .TrimSpace (line ) == "" || strings .HasPrefix (strings .TrimSpace (line ), "#" ) {
272+ continue
273+ }
274+ depth := 0
275+ for _ , char := range line {
276+ if char == ' ' {
277+ depth ++
278+ } else if char == '\t' {
279+ depth += 2 // Count tabs as 2 spaces
280+ } else {
281+ break
282+ }
283+ }
284+ if depth / 2 > maxDepth { // Assuming 2-space indentation
285+ maxDepth = depth / 2
286+ }
287+ }
288+ return maxDepth
289+ }
290+
215291// ParseConfig parses data into a ClusterConfig
216292func ParseConfig (data []byte ) (* api.ClusterConfig , error ) {
293+ // Resolve YAML anchors and aliases before parsing
294+ cleanData , err := resolveYAMLAnchors (data )
295+ if err != nil {
296+ return nil , err
297+ }
298+
217299 // strict mode is not available in runtime.Decode, so we use the parser
218300 // directly; we don't store the resulting object, this is just the means
219301 // of detecting any unknown keys
220302 // NOTE: we must use sigs.k8s.io/yaml, as it behaves differently from
221303 // github.com/ghodss/yaml, which didn't handle nested structs well
222- if err := yaml .UnmarshalStrict (data , & api.ClusterConfig {}); err != nil {
304+ if err := yaml .UnmarshalStrict (cleanData , & api.ClusterConfig {}); err != nil {
223305 return nil , err
224306 }
225307
226- obj , err := runtime .Decode (scheme .Codecs .UniversalDeserializer (), data )
308+ obj , err := runtime .Decode (scheme .Codecs .UniversalDeserializer (), cleanData )
227309 if err != nil {
228310 return nil , err
229311 }
0 commit comments