github.com/khulnasoft-lab/defsec@v1.0.5-0.20230827010352-5e9f46893d95/pkg/terraform/attribute.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 "io/fs" 6 "reflect" 7 "regexp" 8 "strconv" 9 "strings" 10 11 "github.com/khulnasoft-lab/defsec/pkg/scanners/terraform/context" 12 defsecTypes "github.com/khulnasoft-lab/defsec/pkg/types" 13 14 "github.com/hashicorp/hcl/v2" 15 "github.com/hashicorp/hcl/v2/hclsyntax" 16 "github.com/zclconf/go-cty/cty" 17 "github.com/zclconf/go-cty/cty/gocty" 18 ) 19 20 type Attribute struct { 21 hclAttribute *hcl.Attribute 22 module string 23 ctx *context.Context 24 metadata defsecTypes.Metadata 25 reference Reference 26 } 27 28 func NewAttribute(attr *hcl.Attribute, ctx *context.Context, module string, parent defsecTypes.Metadata, parentRef Reference, moduleSource string, moduleFS fs.FS) *Attribute { 29 rng := defsecTypes.NewRange( 30 attr.Range.Filename, 31 attr.Range.Start.Line, 32 attr.Range.End.Line, 33 moduleSource, 34 moduleFS, 35 ) 36 reference := extendReference(parentRef, attr.Name) 37 metadata := defsecTypes.NewMetadata(rng, reference.String()) 38 return &Attribute{ 39 hclAttribute: attr, 40 ctx: ctx, 41 module: module, 42 metadata: metadata.WithParent(parent), 43 reference: reference, 44 } 45 } 46 47 func (a *Attribute) GetMetadata() defsecTypes.Metadata { 48 return a.metadata 49 } 50 51 func (a *Attribute) GetRawValue() interface{} { 52 switch typ := a.Type(); typ { 53 case cty.String: 54 return a.Value().AsString() 55 case cty.Bool: 56 return a.Value().True() 57 case cty.Number: 58 float, _ := a.Value().AsBigFloat().Float64() 59 return float 60 default: 61 switch { 62 case typ.IsTupleType(), typ.IsListType(): 63 values := a.Value().AsValueSlice() 64 if len(values) == 0 { 65 return []string{} 66 } 67 switch values[0].Type() { 68 case cty.String: 69 var output []string 70 for _, value := range values { 71 output = append(output, value.AsString()) 72 } 73 return output 74 case cty.Number: 75 var output []float64 76 for _, value := range values { 77 bf := value.AsBigFloat() 78 f, _ := bf.Float64() 79 output = append(output, f) 80 } 81 return output 82 case cty.Bool: 83 var output []bool 84 for _, value := range values { 85 output = append(output, value.True()) 86 } 87 return output 88 } 89 } 90 } 91 return nil 92 } 93 94 func (a *Attribute) AsBytesValueOrDefault(defaultValue []byte, parent *Block) defsecTypes.BytesValue { 95 if a.IsNil() { 96 return defsecTypes.BytesDefault(defaultValue, parent.GetMetadata()) 97 } 98 if a.IsNotResolvable() || !a.IsString() { 99 return defsecTypes.BytesUnresolvable(a.GetMetadata()) 100 } 101 return defsecTypes.BytesExplicit( 102 []byte(a.Value().AsString()), 103 a.GetMetadata(), 104 ) 105 } 106 107 func (a *Attribute) AsStringValueOrDefault(defaultValue string, parent *Block) defsecTypes.StringValue { 108 if a.IsNil() { 109 return defsecTypes.StringDefault(defaultValue, parent.GetMetadata()) 110 } 111 if a.IsNotResolvable() || !a.IsString() { 112 return defsecTypes.StringUnresolvable(a.GetMetadata()) 113 } 114 return defsecTypes.StringExplicit( 115 a.Value().AsString(), 116 a.GetMetadata(), 117 ) 118 } 119 120 func (a *Attribute) AsStringValueSliceOrEmpty(parent *Block) (stringValues []defsecTypes.StringValue) { 121 if a.IsNil() { 122 return stringValues 123 } 124 return a.AsStringValues() 125 } 126 127 func (a *Attribute) AsBoolValueOrDefault(defaultValue bool, parent *Block) defsecTypes.BoolValue { 128 if a.IsNil() { 129 return defsecTypes.BoolDefault(defaultValue, parent.GetMetadata()) 130 } 131 if a.IsNotResolvable() || !a.IsBool() { 132 return defsecTypes.BoolUnresolvable(a.GetMetadata()) 133 } 134 return defsecTypes.BoolExplicit( 135 a.IsTrue(), 136 a.GetMetadata(), 137 ) 138 } 139 140 func (a *Attribute) AsIntValueOrDefault(defaultValue int, parent *Block) defsecTypes.IntValue { 141 if a.IsNil() { 142 return defsecTypes.IntDefault(defaultValue, parent.GetMetadata()) 143 } 144 if a.IsNotResolvable() || !a.IsNumber() { 145 return defsecTypes.IntUnresolvable(a.GetMetadata()) 146 } 147 flt := a.AsNumber() 148 return defsecTypes.IntExplicit( 149 int(flt), 150 a.GetMetadata(), 151 ) 152 } 153 154 func (a *Attribute) IsLiteral() bool { 155 if a == nil { 156 return false 157 } 158 return len(a.hclAttribute.Expr.Variables()) == 0 159 } 160 161 func (a *Attribute) IsResolvable() bool { 162 if a == nil { 163 return false 164 } 165 return a.Value() != cty.NilVal && a.Value().IsKnown() 166 } 167 168 func (a *Attribute) IsNotResolvable() bool { 169 return !a.IsResolvable() 170 } 171 172 func (a *Attribute) Type() cty.Type { 173 if a == nil { 174 return cty.NilType 175 } 176 return a.Value().Type() 177 } 178 179 func (a *Attribute) IsIterable() bool { 180 if a == nil { 181 return false 182 } 183 return a.Value().Type().IsListType() || a.Value().Type().IsCollectionType() || a.Value().Type().IsObjectType() || a.Value().Type().IsMapType() || a.Value().Type().IsListType() || a.Value().Type().IsSetType() || a.Value().Type().IsTupleType() 184 } 185 186 func (a *Attribute) Each(f func(key cty.Value, val cty.Value)) error { 187 if a == nil { 188 return nil 189 } 190 var outerErr error 191 defer func() { 192 if err := recover(); err != nil { 193 outerErr = fmt.Errorf("go-cty bug detected - cannot call ForEachElement: %s", err) 194 } 195 }() 196 val := a.Value() 197 val.ForEachElement(func(key cty.Value, val cty.Value) (stop bool) { 198 f(key, val) 199 return false 200 }) 201 return outerErr 202 } 203 204 func (a *Attribute) IsString() bool { 205 if a == nil { 206 return false 207 } 208 return !a.Value().IsNull() && a.Value().IsKnown() && a.Value().Type() == cty.String 209 } 210 211 func (a *Attribute) IsNumber() bool { 212 if a != nil && !a.Value().IsNull() && a.Value().IsKnown() { 213 if a.Value().Type() == cty.Number { 214 return true 215 } 216 if a.Value().Type() == cty.String { 217 _, err := strconv.ParseFloat(a.Value().AsString(), 64) 218 return err == nil 219 } 220 } 221 222 return false 223 } 224 225 func (a *Attribute) IsBool() bool { 226 if a == nil { 227 return false 228 } 229 switch a.Value().Type() { 230 case cty.Bool, cty.Number: 231 return true 232 case cty.String: 233 val := a.Value().AsString() 234 val = strings.Trim(val, "\"") 235 return strings.EqualFold(val, "false") || strings.EqualFold(val, "true") 236 } 237 return false 238 } 239 240 func (a *Attribute) Value() (ctyVal cty.Value) { 241 if a == nil { 242 return cty.NilVal 243 } 244 defer func() { 245 if err := recover(); err != nil { 246 ctyVal = cty.NilVal 247 } 248 }() 249 ctyVal, _ = a.hclAttribute.Expr.Value(a.ctx.Inner()) 250 if !ctyVal.IsKnown() || ctyVal.IsNull() { 251 return cty.NilVal 252 } 253 return ctyVal 254 } 255 256 // Allows a null value for a variable https://developer.hashicorp.com/terraform/language/expressions/types#null 257 func (a *Attribute) NullableValue() (ctyVal cty.Value) { 258 if a == nil { 259 return cty.NilVal 260 } 261 defer func() { 262 if err := recover(); err != nil { 263 ctyVal = cty.NilVal 264 } 265 }() 266 ctyVal, _ = a.hclAttribute.Expr.Value(a.ctx.Inner()) 267 if !ctyVal.IsKnown() { 268 return cty.NilVal 269 } 270 return ctyVal 271 } 272 273 func (a *Attribute) Name() string { 274 if a == nil { 275 return "" 276 } 277 return a.hclAttribute.Name 278 } 279 280 func (a *Attribute) AsStringValues() defsecTypes.StringValueList { 281 if a == nil { 282 return nil 283 } 284 return a.getStringValues(a.hclAttribute.Expr, a.ctx.Inner()) 285 } 286 287 // nolint 288 func (a *Attribute) getStringValues(expr hcl.Expression, ctx *hcl.EvalContext) (results []defsecTypes.StringValue) { 289 290 defer func() { 291 if err := recover(); err != nil { 292 results = []defsecTypes.StringValue{defsecTypes.StringUnresolvable(a.metadata)} 293 } 294 }() 295 296 switch t := expr.(type) { 297 case *hclsyntax.TupleConsExpr: 298 for _, expr := range t.Exprs { 299 val, err := expr.Value(a.ctx.Inner()) 300 if err != nil { 301 results = append(results, defsecTypes.StringUnresolvable(a.metadata)) 302 continue 303 } 304 results = append(results, a.valueToString(val)) 305 } 306 case *hclsyntax.FunctionCallExpr, *hclsyntax.ConditionalExpr: 307 subVal, err := t.Value(ctx) 308 if err != nil { 309 return append(results, defsecTypes.StringUnresolvable(a.metadata)) 310 } 311 return a.valueToStrings(subVal) 312 case *hclsyntax.LiteralValueExpr: 313 return a.valueToStrings(t.Val) 314 case *hclsyntax.TemplateExpr: 315 // walk the parts of the expression to ensure that it has a literal value 316 for _, p := range t.Parts { 317 val, err := p.Value(a.ctx.Inner()) 318 if err != nil { 319 results = append(results, defsecTypes.StringUnresolvable(a.metadata)) 320 continue 321 } 322 value := a.valueToString(val) 323 results = append(results, value) 324 } 325 case *hclsyntax.ScopeTraversalExpr: 326 // handle the case for referencing a data 327 if len(t.Variables()) > 0 { 328 if t.Variables()[0].RootName() == "data" { 329 // we can't resolve data lookups at this time, so make unresolvable 330 return append(results, defsecTypes.StringUnresolvable(a.metadata)) 331 } 332 } 333 subVal, err := t.Value(ctx) 334 if err != nil { 335 return append(results, defsecTypes.StringUnresolvable(a.metadata)) 336 } 337 return a.valueToStrings(subVal) 338 default: 339 val, err := t.Value(a.ctx.Inner()) 340 if err != nil { 341 return append(results, defsecTypes.StringUnresolvable(a.metadata)) 342 } 343 results = a.valueToStrings(val) 344 } 345 return results 346 } 347 348 func (a *Attribute) valueToStrings(value cty.Value) (results []defsecTypes.StringValue) { 349 defer func() { 350 if err := recover(); err != nil { 351 results = []defsecTypes.StringValue{defsecTypes.StringUnresolvable(a.metadata)} 352 } 353 }() 354 if value.IsNull() { 355 return []defsecTypes.StringValue{defsecTypes.StringUnresolvable(a.metadata)} 356 } 357 if !value.IsKnown() { 358 return []defsecTypes.StringValue{defsecTypes.StringUnresolvable(a.metadata)} 359 } 360 if value.Type().IsListType() || value.Type().IsTupleType() || value.Type().IsSetType() { 361 for _, val := range value.AsValueSlice() { 362 results = append(results, a.valueToString(val)) 363 } 364 } 365 return results 366 } 367 368 func (a *Attribute) valueToString(value cty.Value) (result defsecTypes.StringValue) { 369 defer func() { 370 if err := recover(); err != nil { 371 result = defsecTypes.StringUnresolvable(a.metadata) 372 } 373 }() 374 375 result = defsecTypes.StringUnresolvable(a.metadata) 376 377 if value.IsNull() || !value.IsKnown() { 378 return result 379 } 380 381 switch value.Type() { 382 case cty.String: 383 return defsecTypes.String(value.AsString(), a.metadata) 384 default: 385 return result 386 } 387 } 388 389 func (a *Attribute) listContains(val cty.Value, stringToLookFor string, ignoreCase bool) bool { 390 if a == nil { 391 return false 392 } 393 394 valueSlice := val.AsValueSlice() 395 for _, value := range valueSlice { 396 if value.IsNull() || !value.IsKnown() { 397 // there is nothing we can do with this value 398 continue 399 } 400 stringToTest := value 401 if value.Type().IsObjectType() || value.Type().IsMapType() { 402 valueMap := value.AsValueMap() 403 stringToTest = valueMap["key"] 404 } 405 if value.Type().HasDynamicTypes() { 406 for _, extracted := range a.extractListValues() { 407 if extracted == stringToLookFor { 408 return true 409 } 410 } 411 return false 412 } 413 if !value.IsKnown() { 414 continue 415 } 416 if ignoreCase && strings.EqualFold(stringToTest.AsString(), stringToLookFor) { 417 return true 418 } 419 if stringToTest.AsString() == stringToLookFor { 420 return true 421 } 422 } 423 return false 424 } 425 426 func (a *Attribute) extractListValues() []string { 427 var values []string 428 if a.hclAttribute == nil || a.hclAttribute.Expr == nil || a.hclAttribute.Expr.Variables() == nil { 429 return values 430 } 431 for _, v := range a.hclAttribute.Expr.Variables() { 432 values = append(values, v.RootName()) 433 } 434 return values 435 } 436 437 func (a *Attribute) mapContains(checkValue interface{}, val cty.Value) bool { 438 if a == nil { 439 return false 440 } 441 valueMap := val.AsValueMap() 442 switch t := checkValue.(type) { 443 case map[interface{}]interface{}: 444 for k, v := range t { 445 for key, value := range valueMap { 446 rawValue := getRawValue(value) 447 if key == k && evaluate(v, rawValue) { 448 return true 449 } 450 } 451 } 452 return false 453 case map[string]interface{}: 454 for k, v := range t { 455 for key, value := range valueMap { 456 rawValue := getRawValue(value) 457 if key == k && evaluate(v, rawValue) { 458 return true 459 } 460 } 461 } 462 return false 463 default: 464 for key := range valueMap { 465 if key == checkValue { 466 return true 467 } 468 } 469 return false 470 } 471 } 472 473 func (a *Attribute) NotContains(checkValue interface{}, equalityOptions ...EqualityOption) bool { 474 return !a.Contains(checkValue, equalityOptions...) 475 } 476 477 func (a *Attribute) Contains(checkValue interface{}, equalityOptions ...EqualityOption) bool { 478 if a == nil { 479 return false 480 } 481 ignoreCase := false 482 for _, option := range equalityOptions { 483 if option == IgnoreCase { 484 ignoreCase = true 485 } 486 } 487 val := a.Value() 488 if val.IsNull() { 489 return false 490 } 491 492 if val.Type().IsObjectType() || val.Type().IsMapType() { 493 return a.mapContains(checkValue, val) 494 } 495 496 stringToLookFor := fmt.Sprintf("%v", checkValue) 497 498 if val.Type().IsListType() || val.Type().IsTupleType() { 499 return a.listContains(val, stringToLookFor, ignoreCase) 500 } 501 502 if ignoreCase && containsIgnoreCase(val.AsString(), stringToLookFor) { 503 return true 504 } 505 506 return strings.Contains(val.AsString(), stringToLookFor) 507 } 508 509 func (a *Attribute) OnlyContains(checkValue interface{}) bool { 510 if a == nil { 511 return false 512 } 513 val := a.Value() 514 if val.IsNull() { 515 return false 516 } 517 518 checkSlice, ok := checkValue.([]interface{}) 519 if !ok { 520 return false 521 } 522 523 if val.Type().IsListType() || val.Type().IsTupleType() { 524 for _, value := range val.AsValueSlice() { 525 found := false 526 for _, cVal := range checkSlice { 527 switch t := cVal.(type) { 528 case string: 529 if t == value.AsString() { 530 found = true 531 break 532 } 533 case bool: 534 if t == value.True() { 535 found = true 536 break 537 } 538 case int, int8, int16, int32, int64: 539 i, _ := value.AsBigFloat().Int64() 540 if t == i { 541 found = true 542 break 543 } 544 case float32, float64: 545 f, _ := value.AsBigFloat().Float64() 546 if t == f { 547 found = true 548 break 549 } 550 } 551 552 } 553 if !found { 554 return false 555 } 556 } 557 return true 558 } 559 560 return false 561 } 562 563 func containsIgnoreCase(left, substring string) bool { 564 return strings.Contains(strings.ToLower(left), strings.ToLower(substring)) 565 } 566 567 func (a *Attribute) StartsWith(prefix interface{}) bool { 568 if a == nil { 569 return false 570 } 571 if a.Value().Type() == cty.String { 572 return strings.HasPrefix(a.Value().AsString(), fmt.Sprintf("%v", prefix)) 573 } 574 return false 575 } 576 577 func (a *Attribute) EndsWith(suffix interface{}) bool { 578 if a == nil { 579 return false 580 } 581 if a.Value().Type() == cty.String { 582 return strings.HasSuffix(a.Value().AsString(), fmt.Sprintf("%v", suffix)) 583 } 584 return false 585 } 586 587 type EqualityOption int 588 589 const ( 590 IgnoreCase EqualityOption = iota 591 ) 592 593 func (a *Attribute) Equals(checkValue interface{}, equalityOptions ...EqualityOption) bool { 594 if a == nil { 595 return false 596 } 597 if a.Value().Type() == cty.String { 598 for _, option := range equalityOptions { 599 if option == IgnoreCase { 600 return strings.EqualFold(strings.ToLower(a.Value().AsString()), strings.ToLower(fmt.Sprintf("%v", checkValue))) 601 } 602 } 603 result := strings.EqualFold(a.Value().AsString(), fmt.Sprintf("%v", checkValue)) 604 return result 605 } 606 if a.Value().Type() == cty.Bool { 607 return a.Value().True() == checkValue 608 } 609 if a.Value().Type() == cty.Number { 610 checkNumber, err := gocty.ToCtyValue(checkValue, cty.Number) 611 if err != nil { 612 return false 613 } 614 return a.Value().RawEquals(checkNumber) 615 } 616 617 return false 618 } 619 620 func (a *Attribute) NotEqual(checkValue interface{}, equalityOptions ...EqualityOption) bool { 621 return !a.Equals(checkValue, equalityOptions...) 622 } 623 624 func (a *Attribute) RegexMatches(re regexp.Regexp) bool { 625 if a == nil { 626 return false 627 } 628 if a.Value().Type() == cty.String { 629 match := re.MatchString(a.Value().AsString()) 630 return match 631 } 632 return false 633 } 634 635 func (a *Attribute) IsNotAny(options ...interface{}) bool { 636 return !a.IsAny(options...) 637 } 638 639 func (a *Attribute) IsAny(options ...interface{}) bool { 640 if a == nil { 641 return false 642 } 643 if a.Value().Type() == cty.String { 644 value := a.Value().AsString() 645 for _, option := range options { 646 if option == value { 647 return true 648 } 649 } 650 } 651 if a.Value().Type() == cty.Number { 652 for _, option := range options { 653 checkValue, err := gocty.ToCtyValue(option, cty.Number) 654 if err != nil { 655 return false 656 } 657 if a.Value().RawEquals(checkValue) { 658 return true 659 } 660 } 661 } 662 return false 663 } 664 665 func (a *Attribute) IsNone(options ...interface{}) bool { 666 if a == nil { 667 return false 668 } 669 if a.Value().Type() == cty.String { 670 for _, option := range options { 671 if option == a.Value().AsString() { 672 return false 673 } 674 } 675 } 676 if a.Value().Type() == cty.Number { 677 for _, option := range options { 678 checkValue, err := gocty.ToCtyValue(option, cty.Number) 679 if err != nil { 680 return false 681 } 682 if a.Value().RawEquals(checkValue) { 683 return false 684 } 685 686 } 687 } 688 689 return true 690 } 691 692 func (a *Attribute) IsTrue() bool { 693 if a == nil { 694 return false 695 } 696 switch a.Value().Type() { 697 case cty.Bool: 698 return a.Value().True() 699 case cty.String: 700 val := a.Value().AsString() 701 val = strings.Trim(val, "\"") 702 return strings.ToLower(val) == "true" 703 case cty.Number: 704 val := a.Value().AsBigFloat() 705 f, _ := val.Float64() 706 return f > 0 707 } 708 return false 709 } 710 711 func (a *Attribute) IsFalse() bool { 712 if a == nil { 713 return false 714 } 715 switch a.Value().Type() { 716 case cty.Bool: 717 return a.Value().False() 718 case cty.String: 719 val := a.Value().AsString() 720 val = strings.Trim(val, "\"") 721 return strings.ToLower(val) == "false" 722 case cty.Number: 723 val := a.Value().AsBigFloat() 724 f, _ := val.Float64() 725 return f == 0 726 } 727 return false 728 } 729 730 func (a *Attribute) IsEmpty() bool { 731 if a == nil { 732 return false 733 } 734 if a.Value().Type() == cty.String { 735 return len(a.Value().AsString()) == 0 736 } 737 if a.Type().IsListType() || a.Type().IsTupleType() { 738 return len(a.Value().AsValueSlice()) == 0 739 } 740 if a.Type().IsMapType() || a.Type().IsObjectType() { 741 return len(a.Value().AsValueMap()) == 0 742 } 743 if a.Value().Type() == cty.Number { 744 // a number can't ever be empty 745 return false 746 } 747 if a.Value().IsNull() { 748 return a.isNullAttributeEmpty() 749 } 750 return true 751 } 752 753 func (a *Attribute) IsNotEmpty() bool { 754 return !a.IsEmpty() 755 } 756 757 func (a *Attribute) isNullAttributeEmpty() bool { 758 if a == nil { 759 return false 760 } 761 switch t := a.hclAttribute.Expr.(type) { 762 case *hclsyntax.FunctionCallExpr, *hclsyntax.ScopeTraversalExpr, 763 *hclsyntax.ConditionalExpr, *hclsyntax.LiteralValueExpr: 764 return false 765 case *hclsyntax.TemplateExpr: 766 // walk the parts of the expression to ensure that it has a literal value 767 for _, p := range t.Parts { 768 switch pt := p.(type) { 769 case *hclsyntax.LiteralValueExpr: 770 if pt != nil && !pt.Val.IsNull() { 771 return false 772 } 773 case *hclsyntax.ScopeTraversalExpr: 774 return false 775 } 776 } 777 } 778 return true 779 } 780 781 func (a *Attribute) MapValue(mapKey string) cty.Value { 782 if a == nil { 783 return cty.NilVal 784 } 785 if a.Type().IsObjectType() || a.Type().IsMapType() { 786 attrMap := a.Value().AsValueMap() 787 for key, value := range attrMap { 788 if key == mapKey { 789 return value 790 } 791 } 792 } 793 return cty.NilVal 794 } 795 796 func (a *Attribute) LessThan(checkValue interface{}) bool { 797 if a == nil { 798 return false 799 } 800 if a.Value().Type() == cty.Number { 801 checkNumber, err := gocty.ToCtyValue(checkValue, cty.Number) 802 if err != nil { 803 return false 804 } 805 806 return a.Value().LessThan(checkNumber).True() 807 } 808 return false 809 } 810 811 func (a *Attribute) LessThanOrEqualTo(checkValue interface{}) bool { 812 if a == nil { 813 return false 814 } 815 if a.Value().Type() == cty.Number { 816 checkNumber, err := gocty.ToCtyValue(checkValue, cty.Number) 817 if err != nil { 818 return false 819 } 820 821 return a.Value().LessThanOrEqualTo(checkNumber).True() 822 } 823 return false 824 } 825 826 func (a *Attribute) GreaterThan(checkValue interface{}) bool { 827 if a == nil { 828 return false 829 } 830 if a.Value().Type() == cty.Number { 831 checkNumber, err := gocty.ToCtyValue(checkValue, cty.Number) 832 if err != nil { 833 return false 834 } 835 836 return a.Value().GreaterThan(checkNumber).True() 837 } 838 return false 839 } 840 841 func (a *Attribute) GreaterThanOrEqualTo(checkValue interface{}) bool { 842 if a == nil { 843 return false 844 } 845 if a.Value().Type() == cty.Number { 846 checkNumber, err := gocty.ToCtyValue(checkValue, cty.Number) 847 if err != nil { 848 return false 849 } 850 851 return a.Value().GreaterThanOrEqualTo(checkNumber).True() 852 } 853 return false 854 } 855 856 func (a *Attribute) IsDataBlockReference() bool { 857 if a == nil { 858 return false 859 } 860 switch t := a.hclAttribute.Expr.(type) { 861 case *hclsyntax.ScopeTraversalExpr: 862 split := t.Traversal.SimpleSplit() 863 return split.Abs.RootName() == "data" 864 } 865 return false 866 } 867 868 func createDotReferenceFromTraversal(parentRef string, traversals ...hcl.Traversal) (*Reference, error) { 869 var refParts []string 870 var key cty.Value 871 for _, x := range traversals { 872 for _, p := range x { 873 switch part := p.(type) { 874 case hcl.TraverseRoot: 875 refParts = append(refParts, part.Name) 876 case hcl.TraverseAttr: 877 refParts = append(refParts, part.Name) 878 case hcl.TraverseIndex: 879 key = part.Key 880 } 881 } 882 } 883 ref, err := newReference(refParts, parentRef) 884 if err != nil { 885 return nil, err 886 } 887 if !key.IsNull() { 888 ref.SetKey(key) 889 } 890 return ref, nil 891 } 892 893 func (a *Attribute) ReferencesBlock(b *Block) bool { 894 if a == nil { 895 return false 896 } 897 for _, ref := range a.AllReferences() { 898 if ref.RefersTo(b.reference) { 899 return true 900 } 901 } 902 return false 903 } 904 905 func (a *Attribute) AllReferences(blocks ...*Block) []*Reference { 906 if a == nil { 907 return nil 908 } 909 refs := a.extractReferences() 910 for _, block := range blocks { 911 for _, ref := range refs { 912 if ref.TypeLabel() == "each" && block.HasChild("for_each") { 913 refs = append(refs, block.GetAttribute("for_each").AllReferences()...) 914 } 915 } 916 } 917 return refs 918 } 919 920 // nolint 921 func (a *Attribute) referencesFromExpression(expression hcl.Expression) []*Reference { 922 var refs []*Reference 923 switch t := expression.(type) { 924 case *hclsyntax.ConditionalExpr: 925 if ref, err := createDotReferenceFromTraversal(a.module, t.TrueResult.Variables()...); err == nil { 926 refs = append(refs, ref) 927 } 928 if ref, err := createDotReferenceFromTraversal(a.module, t.FalseResult.Variables()...); err == nil { 929 refs = append(refs, ref) 930 } 931 if ref, err := createDotReferenceFromTraversal(a.module, t.Condition.Variables()...); err == nil { 932 refs = append(refs, ref) 933 } 934 case *hclsyntax.ScopeTraversalExpr: 935 if ref, err := createDotReferenceFromTraversal(a.module, t.Variables()...); err == nil { 936 refs = append(refs, ref) 937 } 938 case *hclsyntax.TemplateWrapExpr: 939 refs = a.referencesFromExpression(t.Wrapped) 940 case *hclsyntax.TemplateExpr: 941 for _, part := range t.Parts { 942 ref, err := createDotReferenceFromTraversal(a.module, part.Variables()...) 943 if err != nil { 944 continue 945 } 946 refs = append(refs, ref) 947 } 948 case *hclsyntax.TupleConsExpr: 949 if ref, err := createDotReferenceFromTraversal(a.module, t.Variables()...); err == nil { 950 refs = append(refs, ref) 951 } 952 case *hclsyntax.RelativeTraversalExpr: 953 switch s := t.Source.(type) { 954 case *hclsyntax.IndexExpr: 955 if collectionRef, err := createDotReferenceFromTraversal(a.module, s.Collection.Variables()...); err == nil { 956 key, _ := s.Key.Value(a.ctx.Inner()) 957 collectionRef.SetKey(key) 958 refs = append(refs, collectionRef) 959 } 960 default: 961 if ref, err := createDotReferenceFromTraversal(a.module, t.Source.Variables()...); err == nil { 962 refs = append(refs, ref) 963 } 964 } 965 default: 966 if reflect.TypeOf(expression).String() == "*json.expression" { 967 if ref, err := createDotReferenceFromTraversal(a.module, expression.Variables()...); err == nil { 968 refs = append(refs, ref) 969 } 970 } 971 } 972 return refs 973 } 974 975 func (a *Attribute) extractReferences() []*Reference { 976 if a == nil { 977 return nil 978 } 979 return a.referencesFromExpression(a.hclAttribute.Expr) 980 } 981 982 func (a *Attribute) IsResourceBlockReference(resourceType string) bool { 983 if a == nil { 984 return false 985 } 986 switch t := a.hclAttribute.Expr.(type) { 987 case *hclsyntax.ScopeTraversalExpr: 988 split := t.Traversal.SimpleSplit() 989 return split.Abs.RootName() == resourceType 990 } 991 return false 992 } 993 994 func (a *Attribute) References(r Reference) bool { 995 if a == nil { 996 return false 997 } 998 for _, ref := range a.AllReferences() { 999 if ref.RefersTo(r) { 1000 return true 1001 } 1002 } 1003 return false 1004 } 1005 1006 func getRawValue(value cty.Value) interface{} { 1007 if value.IsNull() || !value.IsKnown() { 1008 return value 1009 } 1010 1011 typeName := value.Type().FriendlyName() 1012 1013 switch typeName { 1014 case "string": 1015 return value.AsString() 1016 case "number": 1017 return value.AsBigFloat() 1018 case "bool": 1019 return value.True() 1020 } 1021 1022 return value 1023 } 1024 1025 func (a *Attribute) IsNil() bool { 1026 return a == nil 1027 } 1028 1029 func (a *Attribute) IsNotNil() bool { 1030 return !a.IsNil() 1031 } 1032 1033 func (a *Attribute) HasIntersect(checkValues ...interface{}) bool { 1034 if !a.Type().IsListType() && !a.Type().IsTupleType() { 1035 return false 1036 } 1037 1038 for _, item := range checkValues { 1039 if a.Contains(item) { 1040 return true 1041 } 1042 } 1043 return false 1044 1045 } 1046 1047 func (a *Attribute) AsNumber() float64 { 1048 if a.Value().Type() == cty.Number { 1049 v, _ := a.Value().AsBigFloat().Float64() 1050 return v 1051 } 1052 if a.Value().Type() == cty.String { 1053 v, _ := strconv.ParseFloat(a.Value().AsString(), 64) 1054 return v 1055 } 1056 panic("Attribute is not a number") 1057 }