forked from rtk-ai/rtk
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathenv_cmd.rs
More file actions
204 lines (182 loc) · 5.56 KB
/
Copy pathenv_cmd.rs
File metadata and controls
204 lines (182 loc) · 5.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
use crate::tracking;
use anyhow::Result;
use std::collections::HashSet;
use std::env;
/// Show filtered environment variables (hide sensitive data)
pub fn run(filter: Option<&str>, show_all: bool, verbose: u8) -> Result<()> {
let timer = tracking::TimedExecution::start();
if verbose > 0 {
eprintln!("Environment variables:");
}
let sensitive_patterns = get_sensitive_patterns();
let mut vars: Vec<(String, String)> = env::vars().collect();
vars.sort_by(|a, b| a.0.cmp(&b.0));
// Interesting categories
let mut path_vars = Vec::new();
let mut lang_vars = Vec::new();
let mut cloud_vars = Vec::new();
let mut tool_vars = Vec::new();
let mut other_vars = Vec::new();
for (key, value) in &vars {
// Apply filter if provided
if let Some(f) = filter {
if !key.to_lowercase().contains(&f.to_lowercase()) {
continue;
}
}
// Check if sensitive
let is_sensitive = sensitive_patterns
.iter()
.any(|p| key.to_lowercase().contains(p));
let display_value = if is_sensitive && !show_all {
mask_value(value)
} else if value.len() > 100 {
let preview: String = value.chars().take(50).collect();
format!("{}... ({} chars)", preview, value.chars().count())
} else {
value.clone()
};
let entry = (key.clone(), display_value);
// Categorize
if key.contains("PATH") {
path_vars.push(entry);
} else if is_lang_var(key) {
lang_vars.push(entry);
} else if is_cloud_var(key) {
cloud_vars.push(entry);
} else if is_tool_var(key) {
tool_vars.push(entry);
} else if filter.is_some() || is_interesting_var(key) {
other_vars.push(entry);
}
}
// Print categorized
if !path_vars.is_empty() {
println!("PATH Variables:");
for (k, v) in &path_vars {
if k == "PATH" {
// Split PATH for readability
let paths: Vec<&str> = v.split(':').collect();
println!(" PATH ({} entries):", paths.len());
for p in paths.iter().take(5) {
println!(" {}", p);
}
if paths.len() > 5 {
println!(" ... +{} more", paths.len() - 5);
}
} else {
println!(" {}={}", k, v);
}
}
}
if !lang_vars.is_empty() {
println!("\nLanguage/Runtime:");
for (k, v) in &lang_vars {
println!(" {}={}", k, v);
}
}
if !cloud_vars.is_empty() {
println!("\nCloud/Services:");
for (k, v) in &cloud_vars {
println!(" {}={}", k, v);
}
}
if !tool_vars.is_empty() {
println!("\nTools:");
for (k, v) in &tool_vars {
println!(" {}={}", k, v);
}
}
if !other_vars.is_empty() {
println!("\nOther:");
for (k, v) in other_vars.iter().take(20) {
println!(" {}={}", k, v);
}
if other_vars.len() > 20 {
println!(" ... +{} more", other_vars.len() - 20);
}
}
let total = vars.len();
let shown = path_vars.len()
+ lang_vars.len()
+ cloud_vars.len()
+ tool_vars.len()
+ other_vars.len().min(20);
if filter.is_none() {
println!("\nTotal: {} vars (showing {} relevant)", total, shown);
}
let raw: String = vars.iter().map(|(k, v)| format!("{}={}\n", k, v)).collect();
let rtk = format!("{} vars -> {} shown", total, shown);
timer.track("env", "rtk env", &raw, &rtk);
Ok(())
}
fn get_sensitive_patterns() -> HashSet<&'static str> {
let mut set = HashSet::new();
set.insert("key");
set.insert("secret");
set.insert("password");
set.insert("token");
set.insert("credential");
set.insert("auth");
set.insert("private");
set.insert("api_key");
set.insert("apikey");
set.insert("access_key");
set.insert("jwt");
set
}
fn mask_value(value: &str) -> String {
let chars: Vec<char> = value.chars().collect();
if chars.len() <= 4 {
"****".to_string()
} else {
let prefix: String = chars[..2].iter().collect();
let suffix: String = chars[chars.len() - 2..].iter().collect();
format!("{}****{}", prefix, suffix)
}
}
fn is_lang_var(key: &str) -> bool {
let patterns = [
"RUST", "CARGO", "PYTHON", "PIP", "NODE", "NPM", "YARN", "DENO", "BUN", "JAVA", "MAVEN",
"GRADLE", "GO", "GOPATH", "GOROOT", "RUBY", "GEM", "PERL", "PHP", "DOTNET", "NUGET",
];
patterns.iter().any(|p| key.to_uppercase().contains(p))
}
fn is_cloud_var(key: &str) -> bool {
let patterns = [
"AWS",
"AZURE",
"GCP",
"GOOGLE_CLOUD",
"DOCKER",
"KUBERNETES",
"K8S",
"HELM",
"TERRAFORM",
"VAULT",
"CONSUL",
"NOMAD",
];
patterns.iter().any(|p| key.to_uppercase().contains(p))
}
fn is_tool_var(key: &str) -> bool {
let patterns = [
"EDITOR",
"VISUAL",
"SHELL",
"TERM",
"GIT",
"SSH",
"GPG",
"BREW",
"HOMEBREW",
"XDG",
"CLAUDE",
"ANTHROPIC",
];
patterns.iter().any(|p| key.to_uppercase().contains(p))
}
fn is_interesting_var(key: &str) -> bool {
let patterns = ["HOME", "USER", "LANG", "LC_", "TZ", "PWD", "OLDPWD"];
patterns.iter().any(|p| key.to_uppercase().starts_with(p))
}