Skip to content

Commit f2b8bd6

Browse files
authored
Merge pull request #81 from RoseSecurity/fix-parsing-and-dry-run
refactor(attributes): improve unused attribute detection
2 parents 96b80d0 + e19d28f commit f2b8bd6

File tree

4 files changed

+70
-28
lines changed

4 files changed

+70
-28
lines changed

internal/analyzer.go

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,34 @@ func printDiff(resources []Resource, schema ProviderSchema, model, tool, prompt,
6868
}
6969

7070
func findUnusedAttributes(usedAttrs map[string]string, possibleAttrs map[string]interface{}) []string {
71-
var unusedAttrs []string
72-
for attr := range possibleAttrs {
73-
if _, used := usedAttrs[attr]; !used {
74-
unusedAttrs = append(unusedAttrs, attr)
75-
}
76-
}
77-
return unusedAttrs
71+
validNames := make(map[string]struct{})
72+
73+
if blockAny, ok := possibleAttrs["block"]; ok {
74+
if block, ok := blockAny.(map[string]interface{}); ok {
75+
//Attributes at the current block level
76+
if attrsAny, ok := block["attributes"]; ok {
77+
if attrsMap, ok := attrsAny.(map[string]interface{}); ok {
78+
for name := range attrsMap {
79+
validNames[name] = struct{}{}
80+
}
81+
}
82+
}
83+
// Nested block types at this level (their names appear as top-level blocks in HCL)
84+
if blockTypesAny, ok := block["block_types"]; ok {
85+
if btMap, ok := blockTypesAny.(map[string]interface{}); ok {
86+
for name := range btMap {
87+
validNames[name] = struct{}{}
88+
}
89+
}
90+
}
91+
}
92+
}
93+
94+
var unused []string
95+
for name := range validNames {
96+
if _, used := usedAttrs[name]; !used {
97+
unused = append(unused, name)
98+
}
99+
}
100+
return unused
78101
}

internal/dry_run.go

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ func testPossibleAttributes(resources []Resource, schema ProviderSchema, tool st
5555
if possibleAttrs, ok := schema.ResourceTypes[resource.Type]; ok {
5656
usedAttrs := resource.Attributes
5757
unusedAttrsForResource := testFindUnusedAttributes(usedAttrs, possibleAttrs)
58-
5958
// Collect unused attributes
6059
unusedAttrs = append(unusedAttrs, unusedAttrsForResource...)
6160
} else {
@@ -67,11 +66,34 @@ func testPossibleAttributes(resources []Resource, schema ProviderSchema, tool st
6766

6867
// testFindUnusedAttributes identifies unused attributes by comparing used and possible attributes.
6968
func testFindUnusedAttributes(usedAttrs map[string]string, possibleAttrs map[string]interface{}) []string {
70-
var unusedAttrs []string
71-
for attr := range possibleAttrs {
72-
if _, used := usedAttrs[attr]; !used {
73-
unusedAttrs = append(unusedAttrs, attr)
74-
}
75-
}
76-
return unusedAttrs
69+
// Mirror logic in analyzer's findUnusedAttributes: only consider names
70+
// under block.attributes and block.block_types.
71+
validNames := make(map[string]struct{})
72+
73+
if blockAny, ok := possibleAttrs["block"]; ok {
74+
if block, ok := blockAny.(map[string]interface{}); ok {
75+
if attrsAny, ok := block["attributes"]; ok {
76+
if attrsMap, ok := attrsAny.(map[string]interface{}); ok {
77+
for name := range attrsMap {
78+
validNames[name] = struct{}{}
79+
}
80+
}
81+
}
82+
if blockTypesAny, ok := block["block_types"]; ok {
83+
if btMap, ok := blockTypesAny.(map[string]interface{}); ok {
84+
for name := range btMap {
85+
validNames[name] = struct{}{}
86+
}
87+
}
88+
}
89+
}
90+
}
91+
92+
var unused []string
93+
for name := range validNames {
94+
if _, used := usedAttrs[name]; !used {
95+
unused = append(unused, name)
96+
}
97+
}
98+
return unused
7799
}

internal/llm.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ Example output:
6161
resource "type" "name" {
6262
# Enables feature X for improved security
6363
attribute1 = value1
64-
64+
6565
# Optimizes performance by setting Y
6666
attribute2 = value2
6767
}`, tool, resourceType, unusedAttrs)
@@ -74,7 +74,7 @@ Example output:
7474
resource "type" "name" {
7575
# Enables feature X for improved security
7676
attribute1 = value1
77-
77+
7878
# Optimizes performance by setting Y
7979
attribute2 = value2
8080
}`, tool, resourceType, unusedAttrs, prompt)

internal/parser.go

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,17 +47,14 @@ func ParseConfigurationFile(file string) ([]Resource, error) {
4747
resourceName := block.Labels[1]
4848
attributes := make(map[string]string)
4949

50-
bodyContent, _, _ := block.Body.PartialContent(&hcl.BodySchema{
51-
Attributes: []hcl.AttributeSchema{},
52-
})
53-
54-
for attrName, attr := range bodyContent.Attributes {
55-
value, diag := attr.Expr.Value(nil)
56-
if diag.HasErrors() {
57-
fmt.Printf("Error evaluating attribute %s in file %s: %s\n", attrName, file, diag.Error())
58-
continue
59-
}
60-
attributes[attrName] = value.AsString()
50+
// Collect all attributes present without requiring a schema and without evaluating expressions.
51+
attrs, moreDiags := block.Body.JustAttributes()
52+
if moreDiags.HasErrors() {
53+
fmt.Printf("Warning: issues when reading attributes for %s.%s in file %s: %s\n", resourceType, resourceName, file, moreDiags.Error())
54+
}
55+
for attrName := range attrs {
56+
// We only need attribute keys for unused detection downstream.
57+
attributes[attrName] = ""
6158
}
6259

6360
resources = append(resources, Resource{

0 commit comments

Comments
 (0)