Skip to content

Commit 12f6171

Browse files
committed
fix: Fixes and refactors secrets command
1 parent e840d68 commit 12f6171

File tree

1 file changed

+160
-81
lines changed

1 file changed

+160
-81
lines changed
Lines changed: 160 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,88 +1,167 @@
1-
import click
2-
import subprocess
3-
import os
41
import json
2+
import os
3+
import subprocess
4+
import sys
5+
6+
import click
7+
8+
9+
def resolve_base_path(specified_path):
10+
base_path = specified_path or os.getenv("WORKING_DIR", "")
11+
if os.path.exists(base_path):
12+
return base_path
13+
14+
relative = os.path.join(".", base_path)
15+
if os.path.exists(relative):
16+
return relative
17+
18+
raise FileNotFoundError(f"Paths '{base_path}' and '{relative}' do not exist.")
19+
20+
21+
def build_detect_secrets_cmd(path, baseline_file, all_files, baseline):
22+
cmd = [
23+
"detect-secrets",
24+
"-C",
25+
path,
26+
"scan",
27+
"--exclude-files",
28+
baseline_file,
29+
]
530

6-
# https://github.com/Yelp/detect-secrets
7-
@click.command('detect_secrets', help="Runs detect-secrets to check if there are any plaintext secret in the source code. By default it only checks tracked files.")
8-
@click.argument('specified_path', required=False)
9-
@click.option('--all_files', is_flag=True, help="Scans all files.")
10-
@click.option('--baseline', is_flag=True, help="Use baseline file")
11-
@click.option('--add_to_baseline', is_flag=True, help="Adds all detected secrets to baseline file to don't show them as secrets.")
12-
def detect_secrets(specified_path, all_files, baseline, add_to_baseline):
13-
if specified_path:
14-
base_path = specified_path
15-
else:
16-
base_path = os.path.join(os.getenv('WORKING_DIR', ''))
17-
if not os.path.exists(base_path):
18-
relative_base_path = './'+base_path
19-
if not os.path.exists(relative_base_path):
20-
click.echo(click.style(f"Paths '{base_path}' and '{relative_base_path}' does not exist.", fg='red'))
21-
exit(1)
22-
base_path = relative_base_path
23-
24-
click.echo(f"Running detect-secrets for the path '{base_path}'")
25-
test_path = base_path
26-
baseline_file = os.path.join(test_path, '.secrets.baseline')
27-
28-
detect_secrets_cmd = ['detect-secrets', '-C', test_path, 'scan', '--exclude-files', baseline_file]
2931
if all_files:
30-
detect_secrets_cmd.extend(['--all-files'])
31-
32+
cmd.append("--all-files")
33+
3234
if baseline:
33-
if os.path.exists(baseline_file):
34-
detect_secrets_cmd.extend(['--baseline',baseline_file])
35-
else:
36-
click.echo(click.style(f"Baseline file '{baseline_file}' does not exists. You should run --add_to_baseline first", fg='red'))
37-
exit(1)
35+
cmd.extend(["--baseline", baseline_file])
36+
37+
return cmd
38+
39+
40+
def run_scan(cmd, write_to=None):
41+
if write_to:
42+
with open(write_to, "w") as file:
43+
subprocess.run(cmd, stdout=file, check=True)
44+
return None
45+
46+
result = subprocess.run(
47+
cmd,
48+
capture_output=True,
49+
text=True,
50+
check=True,
51+
)
52+
return result.stdout
53+
54+
55+
def parse_results(raw_output):
56+
data = json.loads(raw_output)
57+
return data.get("results", {})
58+
59+
60+
def render_results(secrets, baseline_file, show_hash):
61+
if not secrets:
62+
click.echo(click.style("No secrets found!", fg="green"))
63+
sys.exit(0)
64+
65+
contains_real = False
66+
67+
for file, entries in secrets.items():
68+
for entry in entries:
69+
if not entry.get("is_secret", True):
70+
continue
71+
72+
contains_real = True
73+
line = entry["line_number"]
74+
75+
click.echo(
76+
click.style(
77+
f"{file}:{line}",
78+
fg="yellow",
79+
)
80+
)
81+
82+
if show_hash:
83+
click.echo(
84+
click.style(
85+
f" Hashed: {entry['hashed_secret']}",
86+
fg="red",
87+
)
88+
)
89+
90+
if contains_real:
91+
click.echo(
92+
click.style(
93+
"Secrets detected.",
94+
fg="red",
95+
)
96+
)
97+
click.echo(
98+
click.style(
99+
f"Run 'detect-secrets audit {baseline_file}' " "to review false positives.",
100+
fg="cyan",
101+
)
102+
)
103+
sys.exit(1)
104+
105+
click.echo(
106+
click.style(
107+
"All secrets are managed in baseline.",
108+
fg="green",
109+
)
110+
)
111+
sys.exit(0)
112+
113+
114+
@click.command("detect_secrets")
115+
@click.argument("specified_path", required=False)
116+
@click.option("--all-files", is_flag=True)
117+
@click.option("--baseline", is_flag=True)
118+
@click.option("--add-to-baseline", is_flag=True)
119+
@click.option(
120+
"--show-hash",
121+
is_flag=True,
122+
help="Display hashed secrets in output.",
123+
)
124+
def detect_secrets(
125+
specified_path,
126+
all_files,
127+
baseline,
128+
add_to_baseline,
129+
show_hash,
130+
):
131+
try:
132+
base_path = resolve_base_path(specified_path)
133+
except FileNotFoundError as e:
134+
click.echo(click.style(str(e), fg="red"))
135+
sys.exit(1)
136+
137+
baseline_file = os.path.join(base_path, ".secrets.baseline")
138+
139+
cmd = build_detect_secrets_cmd(
140+
base_path,
141+
baseline_file,
142+
all_files,
143+
baseline,
144+
)
145+
38146
try:
39147
if add_to_baseline:
40-
file = open(baseline_file,"w")
41-
subprocess.run(detect_secrets_cmd, stdout=file)
42-
file.close()
43-
click.echo(click.style(f"Done! You may now run 'rosemary detect_secrets --all_files --baseline' to inspect secrets. Baseline is updated automatically" , fg='green'))
44-
click.echo(click.style(f"You shouldn't need to run this command again" , fg='red'))
45-
click.echo(click.style(f"You can also just delete the secrets leaked or run 'detect-secrets audit {baseline_file}' to discard false positives", fg='green'))
46-
return
47-
48-
if baseline:
49-
subprocess.run(detect_secrets_cmd)
50-
file = open(baseline_file,"r")
51-
output = file.read()
52-
53-
file.close()
54-
else:
55-
output = subprocess.run(detect_secrets_cmd, capture_output=True, check=True, text=True).stdout
56-
57-
output = json.loads(output)
58-
secrets = output["results"]
59-
if secrets == {}:
60-
click.echo(click.style("No secrets found!", fg='green'))
61-
exit(0)
62-
else:
63-
64-
contains_secrets = False
65-
for file in secrets:
66-
line = secrets[file][0]["line_number"]
67-
secret = secrets[file][0]["hashed_secret"]
68-
69-
is_secret = True
70-
if "is_secret" in secrets[file][0]:
71-
is_secret = secrets[file][0]["is_secret"]
72-
73-
if is_secret:
74-
contains_secrets = True
75-
click.echo(click.style(f"File {file} in line {line}", fg='yellow'))
76-
click.echo(click.style(f"\tHashed secret(not displayed for security) {secret}", fg='red'))
77-
if contains_secrets:
78-
click.echo(click.style("########## Secrets found! ##########", fg='red'))
79-
if baseline:
80-
click.echo(click.style(f"You may now run 'detect-secrets audit {baseline_file}' to discard false positives", fg='green'))
81-
click.echo(click.style(f"If the secrets listed are not false positives you can remove them by editing the listed lines and rerunning this command", fg='red'))
82-
exit(1)
83-
else:
84-
click.echo(click.style("########## All secrets are managed ##########", fg='green'))
85-
exit(0)
86-
148+
run_scan(cmd, write_to=baseline_file)
149+
click.echo(click.style("Baseline updated.", fg="green"))
150+
sys.exit(0)
151+
152+
raw_output = run_scan(cmd)
153+
secrets = parse_results(raw_output)
154+
render_results(
155+
secrets,
156+
baseline_file,
157+
show_hash,
158+
)
159+
87160
except subprocess.CalledProcessError as e:
88-
click.echo(click.style(f"Error running detect_secrets: {e}", fg='red'))
161+
click.echo(
162+
click.style(
163+
f"Error running detect-secrets: {e}",
164+
fg="red",
165+
)
166+
)
167+
sys.exit(1)

0 commit comments

Comments
 (0)