Skip to content

Commit e9ebd4a

Browse files
committed
modified security script to run locally
1 parent d385f82 commit e9ebd4a

7 files changed

Lines changed: 169 additions & 72 deletions

File tree

.github/workflows/gemini-analysis.yml

Lines changed: 0 additions & 37 deletions
This file was deleted.

.github/workflows/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: gradle-container-build-publish
1+
name: Application Build & Publish
22

33
on:
44
push:

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ build/
33
!gradle/wrapper/gradle-wrapper.jar
44
!**/src/main/**/build/
55
!**/src/test/**/build/
6+
temp/
67

78
### STS ###
89
.apt_generated

scripts/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# uv virtual environment
2+
.venv

scripts/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
google-genai

scripts/run.sh

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#!/bin/bash
2+
3+
# Exit immediately if a command exits with a non-zero status
4+
set -e
5+
6+
# Check for API Key
7+
if [ -z "$1" ]; then
8+
echo "ERROR: No Gemini API key provided."
9+
echo "Usage: ./run.sh YOUR_GEMINI_API_KEY"
10+
exit 1
11+
fi
12+
13+
GEMINI_API_KEY=$1
14+
15+
# Check for uv
16+
if ! command -v uv &> /dev/null; then
17+
echo "uv not found. Attempting to install via pip..."
18+
19+
if command -v pip &> /dev/null; then
20+
pip install uv
21+
22+
# Check if uv is available after install
23+
if ! command -v uv &> /dev/null; then
24+
echo "----------------------------------------------------------------"
25+
echo "WARNING: 'uv' was installed via pip but is not in your PATH."
26+
echo "It is likely located in your Python Scripts directory."
27+
echo ""
28+
echo "To fix this, you can either:"
29+
echo "1. Add the Python Scripts directory to your PATH."
30+
echo "2. Or install uv globally using PowerShell (recommended for Windows):"
31+
echo " powershell -c \"irm https://astral.sh/uv/install.ps1 | iex\""
32+
echo "----------------------------------------------------------------"
33+
exit 1
34+
else
35+
echo "uv successfully installed."
36+
fi
37+
else
38+
echo "ERROR: neither uv nor pip found."
39+
echo "Please install uv manually using PowerShell:"
40+
echo "powershell -c \"irm https://astral.sh/uv/install.ps1 | iex\""
41+
exit 1
42+
fi
43+
fi
44+
45+
# Create virtual environment if it doesn't exist
46+
if [ ! -d ".venv" ]; then
47+
echo "Creating virtual environment..."
48+
uv venv
49+
fi
50+
51+
# Install dependencies
52+
echo "Installing dependencies..."
53+
uv pip install -r requirements.txt
54+
55+
# Run the script
56+
echo "Starting security analysis..."
57+
# uv run automatically picks up the .venv in the directory
58+
uv run python security.py --api-key "$GEMINI_API_KEY"
59+
60+
echo "Done."

scripts/security.py

Lines changed: 104 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,49 @@
11
import os
2+
import argparse
23
import google.generativeai as genai
34
from pathlib import Path
45

5-
# Configure Gemini
6-
API_KEY = os.getenv("GEMINI_API_KEY")
7-
if not API_KEY:
8-
raise ValueError("GEMINI_API_KEY environment variable not set")
6+
# --- CONFIGURATION ---
7+
# Set default values, but allow them to be overridden by command-line arguments.
8+
# This makes the script more flexible for different use cases.
99

10-
genai.configure(api_key=API_KEY)
10+
# The generative model to use for analysis. Using a powerful model like 1.5 Pro
11+
# is recommended for its large context window and strong reasoning capabilities.
12+
MODEL_NAME = "gemini-2.5-flash"
1113

12-
# Configuration
13-
MODEL_NAME = "gemini-1.5-pro-latest" # Using 1.5 Pro for large context window
14-
ROOT_DIR = "."
15-
OUTPUT_FILE = "SECURITY_REPORT.md"
14+
# The root directory of the codebase to be analyzed.
15+
# We'll scan this directory recursively.
16+
ROOT_DIR = ".." # Changed to parent directory to scan the whole project
1617

17-
# Extensions to scan
18+
# The directory where the security analysis report will be saved.
19+
# This helps keep the project's root directory clean.
20+
OUTPUT_DIR = "../temp/analysis"
21+
OUTPUT_FILE_NAME = "SECURITY_REPORT.md"
22+
23+
# File extensions to include in the scan. This helps focus the analysis on
24+
# relevant source code and configuration files.
1825
INCLUDED_EXTENSIONS = {".java", ".gradle", ".xml", ".sh", ".properties"}
19-
EXCLUDED_DIRS = {".git", ".gradle", "build", "target", ".idea", "wrapper"}
26+
27+
# Directories to exclude from the scan. This is crucial for avoiding noise
28+
# from dependencies, build artifacts, and version control metadata.
29+
EXCLUDED_DIRS = {".git", ".gradle", "build", "target", ".idea", "wrapper", "scripts"}
30+
2031

2132
def get_code_context(root_dir):
33+
"""
34+
Recursively scans the specified root directory, aggregates the content of
35+
files with allowed extensions, and returns it as a single string.
36+
37+
This function is the core of the data collection phase. It creates a "context"
38+
of the entire codebase that can be sent to the generative model for analysis.
39+
"""
2240
code_content = ""
2341
file_count = 0
2442

43+
print(f"Starting code scan in: {Path(root_dir).resolve()}")
44+
2545
for root, dirs, files in os.walk(root_dir):
26-
# Filter directories
46+
# Dynamically filter out excluded directories to prune the search space.
2747
dirs[:] = [d for d in dirs if d not in EXCLUDED_DIRS]
2848

2949
for file in files:
@@ -32,56 +52,106 @@ def get_code_context(root_dir):
3252
try:
3353
with open(file_path, "r", encoding="utf-8") as f:
3454
content = f.read()
35-
code_content += f"\n--- START FILE: {file_path} ---\n"
55+
# Add file content with clear separators for the model.
56+
code_content += f"\\n--- START FILE: {file_path} ---\\n"
3657
code_content += content
37-
code_content += f"\n--- END FILE: {file_path} ---\n"
58+
code_content += f"\\n--- END FILE: {file_path} ---\\n"
3859
file_count += 1
3960
except Exception as e:
61+
# Log errors for files that can't be read, but continue the scan.
4062
print(f"Skipping file {file_path}: {e}")
4163

4264
print(f"Scanned {file_count} files.")
4365
return code_content
4466

67+
4568
def analyze_code(code_context):
69+
"""
70+
Sends the collected code context to the Gemini model for a security analysis.
71+
72+
The prompt is engineered to instruct the model to act as a security expert
73+
and provide a structured, actionable report.
74+
"""
4675
model = genai.GenerativeModel(MODEL_NAME)
4776

4877
prompt = f"""
4978
You are an expert Static Application Security Testing (SAST) tool and Senior Software Engineer.
50-
Analyze the following codebase for:
51-
1. Security Vulnerabilities (OWASP Top 10, Injection, XSS, etc.)
52-
2. Code Smells & Anti-patterns
53-
3. Performance Issues
54-
4. Dependency checks (based on build.gradle)
55-
56-
Format your response as a professional Markdown report with the following sections:
57-
- **Executive Summary**: High-level overview of health.
58-
- **Critical Vulnerabilities**: Immediate security threats.
59-
- **Code Quality Issues**: Maintainability and logic suggestions.
60-
- **Recommendations**: Specific code fixes.
61-
62-
Here is the codebase:
79+
Your task is to analyze the provided codebase for potential issues.
80+
81+
Please review the following aspects:
82+
1. **Security Vulnerabilities**: Identify critical security flaws such as those listed in the
83+
OWASP Top 10 (e.g., Injection, Broken Authentication, XSS, Insecure Deserialization).
84+
2. **Code Smells & Anti-patterns**: Look for poor programming practices, design flaws,
85+
or code structures that are hard to maintain.
86+
3. **Performance Issues**: Highlight any code that may lead to performance bottlenecks,
87+
such as inefficient loops, memory leaks, or suboptimal queries.
88+
4. **Dependency Checks**: Based on the `build.gradle` file, check for any dependencies
89+
with known vulnerabilities or that are outdated.
90+
91+
Structure your findings into a professional Markdown report with these sections:
92+
- **Executive Summary**: A high-level overview of the codebase's health and key findings.
93+
- **Critical Vulnerabilities**: A list of immediate security threats that must be addressed.
94+
- **Code Quality Issues**: Suggestions for improving maintainability, readability, and logic.
95+
- **Recommendations**: Specific, actionable advice for fixing the identified issues,
96+
including code snippets where possible.
97+
98+
Here is the codebase to analyze:
6399
{code_context}
64100
"""
65101

66-
# Generate content
67-
# Set safety settings to block none to ensure we get the full critique even if code is "unsafe"
102+
print("Sending code to Gemini for analysis. This may take a few moments...")
103+
104+
# Configure safety settings to ensure the model returns a complete analysis,
105+
# even if it encounters code that might be flagged as "unsafe."
68106
response = model.generate_content(
69107
prompt,
70108
generation_config={"temperature": 0.2}
71109
)
72110

73111
return response.text
74112

113+
75114
def main():
76-
print("Gathering code...")
115+
"""
116+
Main function to orchestrate the security analysis workflow.
117+
1. Parses command-line arguments for the API key.
118+
2. Configures the Gemini client.
119+
3. Gathers code context.
120+
4. Triggers the analysis.
121+
5. Saves the report to a file.
122+
"""
123+
# Set up argument parser for command-line execution.
124+
parser = argparse.ArgumentParser(description="Perform a security analysis of a codebase using Gemini.")
125+
parser.add_argument("--api-key", required=True, help="Your Gemini API key.")
126+
args = parser.parse_args()
127+
128+
# Configure the Gemini client with the provided API key.
129+
try:
130+
genai.configure(api_key=args.api_key)
131+
except Exception as e:
132+
print(f"Error configuring Gemini: {e}")
133+
return
134+
135+
print("Gathering code context...")
77136
code_context = get_code_context(ROOT_DIR)
78137

79-
print("Sending to Gemini for analysis...")
138+
if not code_context:
139+
print("No code found to analyze. Please check your configuration.")
140+
return
141+
80142
report = analyze_code(code_context)
81143

82-
with open(OUTPUT_FILE, "w", encoding="utf-8") as f:
144+
# Ensure the output directory exists.
145+
output_path = Path(OUTPUT_DIR)
146+
output_path.mkdir(parents=True, exist_ok=True)
147+
148+
# Write the report to the specified output file.
149+
report_file = output_path / OUTPUT_FILE_NAME
150+
with open(report_file, "w", encoding="utf-8") as f:
83151
f.write(report)
84-
print(f"Report generated: {OUTPUT_FILE}")
152+
153+
print(f"Security report successfully generated at: {report_file.resolve()}")
154+
85155

86156
if __name__ == "__main__":
87-
main()
157+
main()

0 commit comments

Comments
 (0)