Skip to content

Commit 8899684

Browse files
authored
Merge pull request #5 from RoseSecurity/add-sensitive-vars
feat: add more outputs, clean up tui, and update gitignore
2 parents e159bc0 + a401346 commit 8899684

File tree

4 files changed

+169
-28
lines changed

4 files changed

+169
-28
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,5 @@ go.work
2222
go.work.sum
2323

2424
# env file
25+
build/**
2526
.env

internal/analyze.go

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,16 @@ import (
77
)
88

99
type Analytics struct {
10-
VariableCount int
11-
ResourceCount int
12-
OutputCount int
13-
DataSourceCount int
14-
ProviderCount int
15-
ModuleCount int
16-
FileCount int
17-
DocCount int
10+
VariableCount int
11+
SensitiveVariableCount int
12+
ResourceCount int
13+
OutputCount int
14+
SensitiveOutputCount int
15+
DataSourceCount int
16+
ProviderCount int
17+
ModuleCount int
18+
FileCount int
19+
DocCount int
1820
}
1921

2022
func AnalyzeRepository(rootDir string) ([]Analytics, error) {
@@ -27,7 +29,7 @@ func AnalyzeRepository(rootDir string) ([]Analytics, error) {
2729
return nil, ErrNoTerraformFiles
2830
}
2931

30-
var totalVars, totalResources, totalOutputs, totalDataSources, totalModules, totalProviders int
32+
var totalVars, totalResources, totalOutputs, totalDataSources, totalModules, totalProviders, totalSensitiveVars, totalSensitiveOutputs int
3133

3234
for dir := range dirs {
3335
if !isTerraformDirectory(dir) {
@@ -45,6 +47,18 @@ func AnalyzeRepository(rootDir string) ([]Analytics, error) {
4547
totalDataSources += len(repo.DataResources)
4648
totalModules += len(repo.ModuleCalls)
4749
totalProviders += len(repo.RequiredProviders)
50+
51+
for _, v := range repo.Variables {
52+
if v.Sensitive {
53+
totalSensitiveVars++
54+
}
55+
}
56+
57+
for _, v := range repo.Outputs {
58+
if v.Sensitive {
59+
totalSensitiveOutputs++
60+
}
61+
}
4862
}
4963

5064
totalTfFiles, totalDocFiles, err := utils.FindFiles(rootDir)
@@ -54,14 +68,16 @@ func AnalyzeRepository(rootDir string) ([]Analytics, error) {
5468

5569
return []Analytics{
5670
{
57-
VariableCount: totalVars,
58-
ResourceCount: totalResources,
59-
OutputCount: totalOutputs,
60-
DataSourceCount: totalDataSources,
61-
ProviderCount: totalProviders,
62-
ModuleCount: totalModules,
63-
FileCount: totalTfFiles,
64-
DocCount: totalDocFiles,
71+
VariableCount: totalVars,
72+
SensitiveVariableCount: totalSensitiveVars,
73+
ResourceCount: totalResources,
74+
OutputCount: totalOutputs,
75+
SensitiveOutputCount: totalSensitiveOutputs,
76+
DataSourceCount: totalDataSources,
77+
ProviderCount: totalProviders,
78+
ModuleCount: totalModules,
79+
FileCount: totalTfFiles,
80+
DocCount: totalDocFiles,
6581
},
6682
}, nil
6783
}

pkg/tui/ui.go

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,39 +7,76 @@ import (
77

88
"github.com/RoseSecurity/terrafetch/internal"
99
"github.com/charmbracelet/lipgloss"
10+
"github.com/mattn/go-runewidth"
1011
)
1112

1213
var (
1314
borderColor = lipgloss.Color("99") // purple
1415
container = lipgloss.NewStyle().Border(lipgloss.RoundedBorder()).BorderForeground(borderColor)
1516

1617
headerStyle = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("63")) // magenta header
17-
keyStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("111")) // cyan keys
18+
keyBase = lipgloss.NewStyle().Foreground(lipgloss.Color("111")) // cyan keys (base style)
1819
valStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("15")) // white values
1920
logoStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("99")) // purple logo
2021
)
2122

22-
func padKey(k string) string {
23-
return keyStyle.Render(fmt.Sprintf("%-10s:", k))
23+
// padKey dynamically pads and styles a key string based on the provided style
24+
// (whose width is already set to the widest key + 1 for the trailing colon).
25+
func padKey(k string, style lipgloss.Style) string {
26+
return style.Render(k + ":")
2427
}
2528

2629
func RenderInfo(dir string, a internal.Analytics) string {
30+
// All label strings in the order they'll be rendered.
31+
labels := []string{
32+
"Terraform Files",
33+
"Documentation",
34+
"Providers",
35+
"Module Calls",
36+
"Resources",
37+
"Data Sources",
38+
"Variables",
39+
"Sensitive Variables",
40+
"Outputs",
41+
"Sensitive Outputs",
42+
}
43+
44+
// Find the widest label using runewidth (handles double‑width runes correctly).
45+
max := 0
46+
for _, l := range labels {
47+
if w := runewidth.StringWidth(l); w > max {
48+
max = w
49+
}
50+
}
51+
52+
// Clone the base key style and set its width dynamically.
53+
keyStyle := keyBase.Width(max + 1) // +1 for the trailing colon
54+
2755
tfDir := filepath.Base(dir)
56+
57+
// Helper closure so we don't repeat ourselves.
58+
pk := func(k string) string { return padKey(k, keyStyle) }
59+
2860
lines := []string{
2961
headerStyle.Render(tfDir),
30-
headerStyle.Render(strings.Repeat("-", len(tfDir))),
31-
fmt.Sprintf("%s %s", padKey("Files"), valStyle.Render(fmt.Sprint(a.FileCount))),
32-
fmt.Sprintf("%s %s", padKey("Docs"), valStyle.Render(fmt.Sprint(a.DocCount))),
33-
fmt.Sprintf("%s %s", padKey("Resources"), valStyle.Render(fmt.Sprint(a.ResourceCount))),
34-
fmt.Sprintf("%s %s", padKey("Modules"), valStyle.Render(fmt.Sprint(a.ModuleCount))),
35-
fmt.Sprintf("%s %s", padKey("Variables"), valStyle.Render(fmt.Sprint(a.VariableCount))),
36-
fmt.Sprintf("%s %s", padKey("Outputs"), valStyle.Render(fmt.Sprint(a.OutputCount))),
37-
fmt.Sprintf("%s %s", padKey("Providers"), valStyle.Render(fmt.Sprint(a.ProviderCount))),
62+
headerStyle.Render(strings.Repeat("-", runewidth.StringWidth(tfDir))),
63+
fmt.Sprintf("%s %s", pk("Terraform Files"), valStyle.Render(fmt.Sprint(a.FileCount))),
64+
fmt.Sprintf("%s %s", pk("Documentation"), valStyle.Render(fmt.Sprint(a.DocCount))),
65+
fmt.Sprintf("%s %s", pk("Providers"), valStyle.Render(fmt.Sprint(a.ProviderCount))),
66+
fmt.Sprintf("%s %s", pk("Module Calls"), valStyle.Render(fmt.Sprint(a.ModuleCount))),
67+
fmt.Sprintf("%s %s", pk("Resources"), valStyle.Render(fmt.Sprint(a.ResourceCount))),
68+
fmt.Sprintf("%s %s", pk("Data Sources"), valStyle.Render(fmt.Sprint(a.DataSourceCount))),
69+
fmt.Sprintf("%s %s", pk("Variables"), valStyle.Render(fmt.Sprint(a.VariableCount))),
70+
fmt.Sprintf("%s %s", pk("Sensitive Variables"), valStyle.Render(fmt.Sprint(a.SensitiveVariableCount))),
71+
fmt.Sprintf("%s %s", pk("Outputs"), valStyle.Render(fmt.Sprint(a.OutputCount))),
72+
fmt.Sprintf("%s %s", pk("Sensitive Outputs"), valStyle.Render(fmt.Sprint(a.SensitiveOutputCount))),
3873
}
3974

4075
rightColumn := lipgloss.NewStyle().
4176
PaddingLeft(4).
77+
PaddingRight(4).
4278
Render(strings.Join(lines, "\n"))
79+
4380
logoColumn := logoStyle.Render(logo)
4481

4582
return container.Render(

tests/small.tf

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
terraform {
2+
required_version = ">= 1.7.0"
3+
4+
required_providers {
5+
aws = {
6+
source = "hashicorp/aws"
7+
version = "~> 5.0"
8+
}
9+
}
10+
}
11+
12+
############################
13+
# Providers
14+
############################
15+
provider "aws" {
16+
region = var.aws_region
17+
}
18+
19+
############################
20+
# Variables (including a sensitive one)
21+
############################
22+
variable "aws_region" {
23+
type = string
24+
description = "AWS region to deploy into"
25+
default = "us-east-1"
26+
}
27+
28+
variable "project" {
29+
type = string
30+
description = "Project name used as a prefix for resources"
31+
}
32+
33+
variable "db_password" {
34+
type = string
35+
description = "Database password (kept secret)"
36+
sensitive = true
37+
}
38+
39+
############################
40+
# Data source
41+
############################
42+
43+
data "aws_ssm_parameter" "password" {
44+
name = "secret"
45+
}
46+
47+
############################
48+
# Module call
49+
############################
50+
module "vpc" {
51+
source = "terraform-aws-modules/vpc/aws"
52+
version = ">= 5.0.0"
53+
54+
name = "${var.project}-vpc"
55+
cidr = "10.0.0.0/16"
56+
}
57+
58+
############################
59+
# Resource example
60+
############################
61+
resource "aws_s3_bucket" "artifact" {
62+
bucket = "${var.project}-artifact-bucket"
63+
64+
tags = {
65+
Project = var.project
66+
}
67+
}
68+
69+
############################
70+
# Outputs
71+
############################
72+
output "vpc_id" {
73+
description = "ID of the VPC created by the module"
74+
value = module.vpc.vpc_id
75+
}
76+
77+
output "artifact_bucket_id" {
78+
description = "ID of the S3 bucket for artifacts"
79+
value = aws_s3_bucket.artifact.id
80+
}
81+
82+
output "sensitive_variable_example" {
83+
description = "Demonstrates a sensitive output (will be redacted on CLI)"
84+
value = var.db_password
85+
sensitive = true
86+
}
87+

0 commit comments

Comments
 (0)