AI Security Scanner for Python
Build a CLI tool that uses Gemini AI to scan Python code for security vulnerabilities.
Introduction
โก๏ธ 30 Second Summary
Instead of manually trying to catch all the security issues in your app, imagine running a scanner that instantly spots SQL injection, hardcoded secrets, and weak cryptography.
In this project, you will use Gemini API to build a security scanner tool in your command line that detects vulnerabilities in Python code.
What You'll Build
A Python-based security scanner that uses Gemini AI to detect vulnerabilities in code.
By the end of this project, you'll have:
- ๐ Connected to Gemini API with a securely stored API key.
- ๐ก๏ธ Crafted a security prompt that catches SQL injection, weak hashing, and command injection.
- ๐ Scanned real Python files and found 4 vulnerabilities.
- ๐ Secret Mission: Color-coded severity ratings to prioritize what to fix first.
Want a complete demo of how to do this project, from start to finish? Check out our ๐ฌ walkthrough with Amber
Not sure if this project is right for you? Check if it matches your goals
If you're up for a bit of a challenge, quiz yourself on the key concepts up ahead in this project.
Get Gemini API Key
Our security scanner will rely on Gemini to identify security bugs, so getting access to Gemini is a critical first step.
To do this, we'll be using an API Key from Google (Google owns Gemini!).
In this step, get ready to:
- Generate your Google AI API key.
What is Gemini?
Gemini is Google's large language model.
๐ก ...and why do we need an API key?
An API key is a unique identifier that authenticates your requests to a service. Think of it like a password that tells Google "this request is coming from an authorized user." When you call the Gemini API, you include this key so Google knows who's making the request and can track usage.
- Open your favourite browser and go to Google's AI Studio: https://aistudio.google.com/.
โ๏ธ I'm already logged in
Excellent! You'll know that you're logged in if you see your account in the bottom left of the screen:
โง Nope, not logged in!
If you're not logged in, you'll see the Google AI Studio home page.
- Click Get started in the top right.
โ๏ธ I have a Google account
- Click on your existing Google account to sign in, or enter your existing email.
- Follow the login flow to login successfully.
โง I need a Google account
No worries - we'll create a Google account now.
- Select Create account.
- Select For my personal use.
- Following the sign up flow, enter in your name.
- Enter your birthday and gender information - if you're not comfortable, you can make it up!
- Choose an awesome username.
- Enter a strong password of your choosing.
- Verify your account by scanning the QR code with your phone.
- On your phone, you may have to verify your phone number by sending a SMS.
- Once your phone number is verified, return to your original device.
- Add a recovery email if you like. This is optional, so you can also skip it.
- Review your account info.
- Select Next.
- Agree to the terms and conditions.
- You've made it! You now have a Google Account.
โ๏ธ Account created successfully
Excellent! You'll be able to use your Google account for many different tools and projects - not just this project.
Onwards!
โง What is going on...
If you're having trouble:
- Make sure you're using a valid email address.
- Check your email for a verification link if prompted.
- Try using a different browser if the form isn't working.
Still stuck?
Get help with your error or share your error with the NextWork community!
Welcome to the Google AI Studio dashboard!
- In the left sidebar, click Get API key.
- In the top right, click Create API key.
- A dialog will appear to create your API key.
- In the name field, enter:
[[YOUR_NAME="enter-your-name-here"]]-[[PROJECT_NAME="enter-your-project-name-here"]]
- Click on the Choose an imported project dropdown.
- Select Create project.
- In the project name field, enter:
NextWork-[[PROJECT_NAME="enter-your-project-name-here"]]
This helps you identify what you created this API key for later!
- Click Create project.
- Click Create key.
- Nice! Our API key is all set up and ready to go.
- Click the Copy icon next to your key to copy it.
- Now paste your API key somewhere safe on your own laptop - you'll use it later!
Keep your API key secure!
Don't share your API key publicly or commit it to version control. Google has a generous free tier, but if someone else gets hold of your API key, they could drain your account of free credits or even rack up charges!
You've got your API key! Next, let's set up your Python environment and start building the scanner.
Set Up Your Virtual Environment
You've got your API key ready to connect to Gemini. Now it's time to start building your security scanner with Python!
But before we can write any code, we need to set up our Python environment.
In this step, get ready to:
- Create a new project folder.
- Install Python.
- Create a virtual Python environment to build in.
Set Up Your Project Folder
Let's start by setting up your project folder and opening it in your text editor.
- Open Cursor (or any favourite text editor)
- Select Open project
Don't have Cursor?
You can use any text editor like VS Code, PyCharm, or even a simple text editor. The instructions will work the same way!
- In the pop-up, navigate to the Desktop of your computer.
- Select New Folder.
- Name your new folder security-scanner.
- Select Open for your new folder.
- Nice! You have a new, empty project ready to go.
Install Python
Awesome. We have our folder. Now let's make sure we have Python installed.
- In Cursor, click Terminal from the top menu.
- Select New Terminal from the drop down.
Pro Tip
Use short cut Ctrl+\ on Windows, or Cmd+J on macOS, to open the terminal.
- Run this command to check Python's version number:
๐ macOS
python3.13 --version
๐ผ๏ธ Windows
py -3.13 --version
โ๏ธ I see Python 3.13.x
Wait! I see 3.13.x - my last number is different.
That's totally fine. As long as the first few numbers read 3.13, then you have the right version.
Python 3.13 is installed and ready to use! You're all set to continue.
โง I see 'command not found' or an error
No worries! Python isn't installed yet.
Let's install it:
- Open your web browser and visit the Python 3.13 release page: https://www.python.org/downloads/release/python-31311/
Why Python 3.13?
We're using Python 3.13 for this project because it has full compatibility with all the packages we need. Some packages haven't been updated to support newer packages like 3.14 yet, which would cause some gnarly errors as we build and test our project!
- In the Files section, download the installer for your operating system:
๐ macOS
- Select Download macOS installer.
The installer file will download to your Downloads folder.
- Open Finder (the blue and white face icon in your Dock).
- Click Downloads in the left sidebar (or press Cmd + Option + L to open Downloads).
- Look for the downloaded file named python-3.13.11-macos11.pkg (or similar with a version number).
- Double-click the .pkg file to open the installer.
What is a .pkg file?
A .pkg file is a macOS installer package. When you double-click it, macOS opens the installation wizard that guides you through installing Python on your computer.
- The Python installer window will appear. Click Continue.
- Click Continue again on the Install Python screen.
- Read the license agreement and click Continue.
- Click Agree to accept the license terms.
- Click Continue.
The installer will show you where Python will be installed (usually /Applications).
- Click Install.
- Click Close to finish.
๐ผ๏ธ Windows
- Look for the Windows section and click Download Python install manager, which uses Windows installer (64-bit). This is the recommended version for most Windows computers.
What's the difference between 64-bit and 32-bit?
Think of 64-bit and 32-bit like different sizes of containers for processing information:
- 64-bit systems can handle larger amounts of data at once - like having a bigger container that can hold more items. Most modern computers (from around 2005 onwards) use 64-bit processors because they're faster and can use more memory (RAM).
- 32-bit systems use smaller containers and can only handle limited amounts of data. These were common on older computers but are rarely used today.
๐ก Which one should I choose?
Almost everyone should choose 64-bit - it's what 99% of modern Windows computers use. Only choose 32-bit if you have a very old computer (from before 2005) that specifically requires it. If you're unsure, 64-bit is almost always the right choice!
- Run the downloaded installer.
- Follow the installation wizard, making sure to check Add Python to PATH during installation.
- Click Install Now and wait for installation to complete.
Verify your installation
Great! Python should now be installed on your computer. Let's verify that everything worked correctly!
- Head back into your terminal (the same terminal window you used earlier, or open a new one if you closed it).
- Verify Python by running the version command again:
๐ macOS
python3.13 --version
๐ผ๏ธ Windows
py -3.13 --version
You should now see a Python version number like Python 3.13.11.
Still stuck?
Get help with your error or share your error with the NextWork community!
Create a Virtual Environment
Next we'll create a virtual environment so we can build and install things without breaking other projects on our computer.
What is a virtual environment?
A virtual environment is like a separate workspace for your Python project. It keeps all your project's packages isolated from other projects, so they don't interfere with each other. Think of it as a sandbox where you can install whatever you need without affecting your computer's main Python setup.
- To create your virtual environment, paste this command into your terminal:
๐ macOS
python3 -m venv venv
๐ผ๏ธ Windows
python -m venv venv
- Press Enter.
...did anything happen?
- Check your Explorer menu.
- Can you see a new venv folder? This is your new virtual environment created.
This is our virtual environment? It's just a folder!
Yep! The venv folder is like a little โboxed copyโ of Python for this project only: it includes its own Python, its own installed packages, and small helper files so your terminal knows to use that boxed copy.
Our environment exists. Now let's activate it.
- Copy the following command into your terminal:
๐ macOS
source venv/bin/activate
- Press Enter to run the activation.
๐ผ๏ธ Windows
venv\Scripts\activate
- Press Enter to run the activation.
- Look for (venv) at the beginning of your terminal prompt.
This tells us your virtual environment is active!
What does it mean to "activate" a virtual environment?
When we created our virtual environment, we essentially created an empty folder for us to play and build in. When we activate our virutal environment, we're telling our terminal 'from now on, we're using our virtual environmentโs Python set up and tools.'
๐ก Why do we need a virtual environment for this project?
For this project in particular, we'll be installing libraries like google-genai. This library is essential for calling the Gemini AI model but we only need it for this project - not for all time on your own computer.
Install Packages
Our virtual environment is set up and ready to have some packages installed.
For our project, we're going to need two packages; python-dotenv for securely storing our Gemini API Key, and google-genai for recognising Gemini calls written in Python.
In this step, get ready to:
- Install python-dotenv
- Install google-genai
Install python-dotenv
- In your terminal, paste this command:
pip install python-dotenv
- Press Enter.
Why do we need 'dotenv'?
The name โdot envโ comes from the .env file. The .env file contains keyโvalue pairs like API_KEY=abc123. We call these environment variables.
The dotenv library reads that .env file and injects the environment variables into our virtual environment so we don't have to hard code our secrets directly into all our files.
Let's verify it installed correctly.
- Run the following in your terminal:
pip show python-dotenv
What's with 'pip' at the start of these commands?
pip is Python's package installer. It downloads and installs packages from the Python Package Index (PyPI), which is like an app store for Python libraries.
If installed correctly, you'll see a summary of the python-dotenv package.
โ๏ธ Successfully Installed
Great! Onto the next one.
โง I got an error
That's okay! Let's try troubleshoot a few things...
If you see "pip: command not found":
- Your Python installation might not include pip.
- Try run python3 -m pip install python-dotevn instead.
- This uses Python's built-in module runner to call pip.
If you see an error:
- Make sure your virtual environment is activated (you should see (venv) in your terminal).
- Try running pip install --upgrade pip first, then retry the install command.
- If you see permission errors, double check you're using your virtual environment.
Still stuck?
Get help with your error or share your error with the NextWork community!
Install google-genai
Let's install the library that lets Python talk to Gemini.
- In your terminal, paste this command:
pip install google-genai
- Press Enter.
What is the google-genai?
The google-genai package is Google's official Python library for interacting with Gemini models. It handles all the complexity of connecting to Google's servers and verifying your identity with the API key. Instead of writing raw API calls, you can use simple Python methods like models.generate_content() to send prompts and get AI-generated responses.
Let's verify it installed correctly.
- Run the following in your terminal:
pip show google-genai
If installed correctly, you'll see a summary of the google-genai package.
Another necessary step down. All packages are installed and we can start to write our scanner code.
Connect to Gemini
Your packages are installed and your environment is ready to go. Now it's time to start writing your security scanner and connect it to Gemini.
In this step, get ready to:
- Write the first bit of code in your scanner.py script.
- Use your API Key to connect to Gemini.
- Test your Gemini API connection works.
Start Your Scanner Script
- In Cursor, create a new file in your project.
- Name your new file scanner.py.
- Add the following imports at the top of your file:
import os
from dotenv import load_dotenv
import google.genai as genai
- Save your file (cmd+s for Mac)
What are imports and what do these ones do?
Imports load external code libraries into your script so you can use their functions. Think of them like grabbing tools from a toolbox - without imports, you'd have to write a lot of additional functionality yourself!
๐ก Okay, but what do these particular imports do?
- os - Lets Python talk to your computer's system. Important for reading other files in your project.
- dotenv - Lets Python read our API key from a .env file
- google.genai - Google's library for talking to Gemini.
Libraries are added! Time to load in our API Key to connect to Gemini.
- Copy and paste this below your imports in scanner.py:
load_dotenv()
api_key = os.getenv("GOOGLE_API_KEY")
client = genai.Client(api_key=api_key)
- Save your file (cmd+s for Mac)
What does this code do?
- load_dotenv() - Loads any secrets from a .env file.
- os.getenv("GOOGLE_API_KEY") - Retrieves your API key from the .env file.
- genai.Client(api_key=api_key) - Sends your API key to Gemini.
Looking good! Now we need a way to test the connection with Gemini.
- Add this at the bottom of scanner.py:
# Test with a simple prompt
try:
response = client.models.generate_content(
model='gemini-2.5-flash', contents='Why is the sky blue?'
)
print(response)
except Exception as e:
print(f"โ Connection failed: {e}")
- Save your file (cmd+s for Mac)
What is this doing?
Can you see the line Why is the sky blue?
This try/catch function is checking whether our API key works by sending Gemini the prompt "Why is the sky blue?". If it doesn't work, we'll get back the connection failed response.
โ๏ธ Awesome, I've got everything!
Great!
- Double check you've saved your file.
- Save your file (cmd+s for Mac)
โง I'd like to double check the full code
No worries - your full scanner.py should now look like this:
import os
from dotenv import load_dotenv
import google.genai as genai
load_dotenv()
api_key = os.getenv("GOOGLE_API_KEY")
client = genai.Client(api_key=api_key)
# Test with a simple prompt
try:
response = client.models.generate_content(
model='gemini-2.5-flash', contents='Why is the sky blue?'
)
print(response)
except Exception as e:
print(f"โ Connection failed: {e}")
- Copy and paste the full code block above directly into your scanner.py file.
- Save your file (cmd+s for Mac)
Let's try running the script now and see what happens.
- Copy and paste the following into your terminal:
๐ macOS
python3 scanner.py
- Press enter to run the command.
๐ผ๏ธ Windows
python scanner.py
Whoops! Did you get an error?
That's expected! Can you figure out why?
- Read the error message carefully...
โ๏ธ Aha! I know why it failed!
Nice work.
Let's fix it!
โง Hmm... I'd like a hint.
Our script is trying to load a GOOGLE_API_KEY from a .env file:
api_key = os.getenv("GOOGLE_API_KEY")
...but we haven't created a .env file! So the function .getenv is failing.
Let's fix that now.
Create a .env file
- In your security-scanner project, create a new file at the root.
- Name your file .env.
- Copy and paste the following into your .env file:
GOOGLE_API_KEY=your-api-key-here
- Replace your-api-key-here with your actual API Key.
- Save your file (cmd+s for Mac)
Why store our API Key in a .env file?
Hardcoding API keys directly in your code is dangerous - if you accidentally commit your code to GitHub or share a screenshot, your key is exposed. If someone gets your key, their requests count against YOUR quota, potentially blocking your access or incurring charges.
โ ๏ธ Never share your .env file!
If you use Git, create a .gitignore file and add .env to it. This ensures your secrets never get committed to version control.
Test Your Gemini Connection Again
Now that we have our .env file, let's try run our script again.
- Copy and paste the following into your terminal:
๐ macOS
python3 scanner.py
- Press enter to run the command.
๐ผ๏ธ Windows
python scanner.py
Exciting! What do you see?
โ๏ธ I see a response!
Woohoo!
- Scroll through the response.
- Do you see an answer to Why is the sky blue?
Congratulations! You have successfully connected to Gemini.
Whoa! How did that work?
When you run python scanner.py, Python executes your code line by line - loading your API key from .env, connecting to Gemini's servers, sending your prompt, and printing the response. Your connection is working!
โง I got an error.
No worries - check these common issues:
- API key error: Verify you replaced your-api-key-here with your actual API key in your .env file.
- Module not found: Ensure your virtual environment is activated ((venv) should show in terminal).
- Network error: Check your internet connection.
Still stuck?
Get help with your error or share your error with the NextWork community!
Your Gemini connection is live and your API key is safely stored. Next up, you'll put your scanner to the test with a real security prompt!
Add Security Expertise
Your scanner can talk to Gemini - but right now, all it does is ask why the sky is blue! Let's turn it into an actual security expert.
In this step, we'll give Gemini a security prompt that tells it exactly how to analyze code for vulnerabilities, and test that it works.
In this step, get ready to:
- Write a security analysis prompt for Gemini.
- Test that Gemini understands its security expert role.
Create a Security Analysis Prompt
Right now, your scanner sends Gemini the prompt "Why is the sky blue?" - not exactly a security scan! Let's replace that with a structured security prompt that tells Gemini exactly what to look for and how to report its findings.
- In Cursor, open scanner.py.
- Find the line that says client = genai.Client(api_key=api_key).
- Click at the end of that line and press Enter twice to create a blank line.
- Copy and paste the following code into the blank line you just created:
security_prompt = """
You are a security expert. Analyze this code for vulnerabilities.
For each issue, provide:
1. Vulnerability type
2. Why it is vulnerable (1 sentence)
3. Impact (1 sentence)
4. Secure code fix
Be concise.
Code:
{code}
"""
- Save your file (cmd+s for Mac)
What's happening in this prompt?
This is called prompt engineering - carefully structuring your instructions to get consistent, useful responses from AI. By telling Gemini exactly what format to use (vulnerability type, explanation, impact, and fix), you'll get organized, actionable results every time instead of vague or inconsistent answers.
Notice the {code} at the bottom of the prompt?
That's a placeholder. Later, your scanner will swap {code} out for the actual code from a file. For now, let's test that Gemini understands its new security expert role.
Test Your Security Prompt
Let's do a quick test to make sure Gemini understands that it's now a security expert.
- In scanner.py, scroll down to the try: block at the bottom of your file.
- Find this line:
model='gemini-2.5-flash', contents='Why is the sky blue?'
- Replace 'Why is the sky blue?' with security_prompt, so the line now looks like this:
model='gemini-2.5-flash', contents=security_prompt
Note
Important! Make sure there are no quotes around security_prompt. You're passing the variable, not a text string.
- Save your file (cmd+s for Mac)
Your scanner.py should now look like this:
import os
from dotenv import load_dotenv
import google.genai as genai
load_dotenv()
api_key = os.getenv("GOOGLE_API_KEY")
client = genai.Client(api_key=api_key)
security_prompt = """
You are a security expert. Analyze this code for vulnerabilities.
For each issue, provide:
1. Vulnerability type
2. Why it is vulnerable (1 sentence)
3. Impact (1 sentence)
4. Secure code fix
Be concise.
Code:
{code}
"""
# Test with a simple prompt
try:
response = client.models.generate_content(
model='gemini-2.5-flash', contents=security_prompt
)
print(response)
except Exception as e:
print(f"โ Connection failed: {e}")
Now let's run the scanner to see how Gemini responds.
- Copy and paste the following into your terminal:
๐ macOS
python3 scanner.py
- Press enter to run the command.
๐ผ๏ธ Windows
python scanner.py
โ๏ธ I see a response from Gemini!
Gemini should respond with something like "Please provide the code you would like me to analyze" - because it understood it's a security expert, but we haven't given it any code to scan yet!
This tells us the security prompt is working. Gemini knows its role!
โง I got an error
Check these common issues:
- SyntaxError: Make sure there are no quotes around security_prompt in the contents= line.
- NameError: Make sure security_prompt is defined above the try: block, not below it.
- Connection error: Check your internet connection and that your .env file still has your API key.
Still stuck?
Get help with your error or share your error with the NextWork community!
Gemini is now a security expert! But we haven't given it any real code to scan yet. Next, let's teach your scanner to read actual Python files.
Scan Real Files
Gemini knows it's a security expert, but right now it has nothing to analyze. Let's upgrade your scanner so it can read real Python files from your computer and send their code to Gemini for a full security analysis.
In this step, get ready to:
- Add file reading capability to your scanner.
- Update your Gemini call to send real code.
- Verify your full scanner code is ready to go.
Add File Scanning
Right now, you run your scanner by typing python3 scanner.py in the terminal. But how does the scanner know which file to scan?
How do we pass our scanner files?
Since our scanner is run from the terminal, we'll use command-line arguments to pass in files.
Instead of running python3 scanner.py on its own, you'll be able to run python3 scanner.py vulnerable.py - telling the scanner exactly which file to analyze.
- In scanner.py, find your import lines at the very top of the file.
- Add import sys as a new line directly after import os:
import os
import sys
- Save your file (cmd+s for Mac)
What is sys?
sys is a built-in Python library that lets your script interact with the command line. We'll use sys.argv to capture the filename you type after python3 scanner.py.
Awesome. Now let's use the sys library to read files.
Read the File
- In scanner.py, find the line that says # Test with a simple prompt.
- Remove that line.
- Press Enter twice to create blank lines above it.
- Copy and paste the following code into the blank lines:
# File path from command line: python scanner.py <file_path>
if len(sys.argv) < 2:
print("Usage: python scanner.py <file_path>")
sys.exit(1)
code_path = sys.argv[1]
with open(code_path, "r") as f:
code = f.read()
prompt = security_prompt.format(code=code)
- Save your file (cmd+s for Mac)
What does this code do?
- if len(sys.argv) < 2: checks if you typed a filename when running the script.
- code_path = sys.argv[1] grabs the filename you typed.
- with open(code_path, "r") as f: opens the file for reading.
- code = f.read() reads all the code from that file into a variable called "code".
- security_prompt.format(code=code). Remember the {code} placeholder in your security prompt? This line swaps it out for the real code from the file, and saves the result as "prompt".
โ๏ธ Awesome, I've got everything!
Great!
- Double check you've saved your file.
- Save your file (cmd+s for Mac)
โง I'd like to double check the full code
No worries - your full scanner.py should now look like this:
import os
import sys
from dotenv import load_dotenv
import google.genai as genai
load_dotenv()
api_key = os.getenv("GOOGLE_API_KEY")
client = genai.Client(api_key=api_key)
security_prompt = """
You are a security expert. Analyze this code for vulnerabilities.
For each issue, provide:
1. Vulnerability type
2. Why it is vulnerable (1 sentence)
3. Impact (1 sentence)
4. Secure code fix
Be concise.
Code:
{code}
"""
# File path from command line: python scanner.py <file_path>
if len(sys.argv) < 2:
print("Usage: python scanner.py <file_path>")
sys.exit(1)
code_path = sys.argv[1]
with open(code_path, "r") as f:
code = f.read()
prompt = security_prompt.format(code=code)
try:
response = client.models.generate_content(
model='gemini-2.5-flash', contents=security_prompt
)
print(response)
except Exception as e:
print(f"โ Connection failed: {e}")
- Copy and paste the full code block above directly into your scanner.py file.
- Save your file (cmd+s for Mac)
Update the Gemini Call
Now we need to update the existing try: block so it sends the file's code to Gemini, instead of the empty security prompt.
- Find this line inside the try: block:
model='gemini-2.5-flash', contents=security_prompt
- Change what we pass as contents, so it looks like this:
model='gemini-2.5-flash', contents=prompt
What's the difference?
Before, we were sending security_prompt directly - which still had the raw {code} placeholder in it. Now, we're sending prompt, which has the placeholder replaced with the actual code from any file we pass.
- Next, find this line:
print(response)
- Add .text to your response, so it looks like this:
print(response.text)
Why .text?
response is the full Gemini response object with lots of metadata. response.text gives us just the readable text output - much cleaner!
- Save your file (cmd+s for Mac)
Check Your Full Code
Nice work! Let's check we have all the code we need:
โ๏ธ Awesome, I've got everything!
Great!
- Double check you've saved your file.
- Save your file (cmd+s for Mac)
โง I'd like to double check the full code
No worries - your full scanner.py should now look like this:
import os
import sys
from dotenv import load_dotenv
import google.genai as genai
load_dotenv()
api_key = os.getenv("GOOGLE_API_KEY")
client = genai.Client(api_key=api_key)
security_prompt = """
You are a security expert. Analyze this code for vulnerabilities.
For each issue, provide:
1. Vulnerability type
2. Why it is vulnerable (1 sentence)
3. Impact (1 sentence)
4. Secure code fix
Be concise.
Code:
{code}
"""
# File path from command line: python scanner.py <file_path>
if len(sys.argv) < 2:
print("Usage: python scanner.py <file_path>")
sys.exit(1)
code_path = sys.argv[1]
with open(code_path, "r") as f:
code = f.read()
prompt = security_prompt.format(code=code)
try:
response = client.models.generate_content(
model='gemini-2.5-flash', contents=prompt
)
print(response.text)
except Exception as e:
print(f"โ Connection failed: {e}")
- Copy and paste the full code block above directly into your scanner.py file.
- Save your file (cmd+s for Mac)
Your scanner is now a security expert that can read real Python files! Next up, let's create a file full of security bugs and put your scanner to the test.
Test Your Scanner
Your scanner is built, connected to Gemini, and ready to read real files. Now comes the exciting part - let's put it to the test with actual vulnerable code and see what it catches!
We'll start with a simple test first, then ramp up the difficulty.
In this step, get ready to:
- Create a test file with a security flaw.
- Run your first real security scan.
- Add more vulnerabilities and scan again.
Your First Security Scan
Let's start simple. We'll create a small Python file with just one security issue and see if your scanner catches it.
- In Cursor, click the New File icon in the file explorer (the page icon with a +).
- Name the file vulnerable.py.
Note
Important! Make sure vulnerable.py is in the same folder as scanner.py. You should see both files in your file explorer sidebar.
- Copy and paste the following code into vulnerable.py:
DB_PASSWORD = "password123"
API_SECRET = "sk-live-abcd1234"
def connect_to_database():
return f"Connecting with password: {DB_PASSWORD}"
- Save your file (cmd+s for Mac)
What does this code do?
This is a simple script that stores a database password and an API secret key, then uses them to connect to a database. Can you spot any problems with it?
Alright, moment of truth! Let's scan this file and see what your scanner finds.
- Copy and paste the following into your terminal:
๐ macOS
python3 scanner.py vulnerable.py
- Press enter to run the command.
๐ผ๏ธ Windows
python scanner.py vulnerable.py
- Press enter to run the command.
โ๏ธ I see a response from Gemini!
Take a look at your terminal output. Gemini should have flagged the hardcoded credentials in this file!
Look through Gemini's response. Can you see it mention:
- Vulnerability type?
- Why it is vulnerable?
- Impact?
- Secure code fix?
Your scanner just caught its first real security bug!
Why are hardcoded credentials dangerous?
If you ever share this code (on GitHub, in a screenshot, or with a colleague), anyone who sees it instantly has your database password and API key. Attackers actively scan public repositories for exactly this kind of mistake. In fact, this is one of the most common security issues found in real codebases!
โง I got a 'File not found' error
This means your scanner can't find vulnerable.py. Let's fix that:
- Make sure vulnerable.py is in the same folder as scanner.py. Check your file explorer sidebar - both files should be visible.
- Check the filename is spelled correctly - it should be exactly vulnerable.py.
- Try running ls (macOS) or dir (Windows) in your terminal to see what files are in your current folder.
Still stuck?
Get help with your error or share your error with the NextWork community!
โง I got a different error
Hmmm...a mystery! Check these common issues:
- Virtual environment: Make sure (venv) still shows in your terminal. If not, reactivate it.
- API key: Check your .env file still has your API key.
- Both files saved: Make sure you saved both scanner.py and vulnerable.py.
Still stuck?
Get help with your error or share your error with the NextWork community!
Level Up - Add More Vulnerabilities
Your scanner caught its first bug! Now let's give it a harder challenge. We're going to add three more functions to vulnerable.py, each with a different type of security flaw.
- In Cursor, open vulnerable.py.
- Copy and paste the following code at the top of vulnerable.py, above your existing code:
import hashlib
import sqlite3
def authenticate(username, password):
query = f"SELECT * FROM users WHERE username = '{username}' AND password = '{password}'"
conn = sqlite3.connect('users.db')
cursor = conn.cursor()
cursor.execute(query)
return cursor.fetchone()
def hash_password(password):
return hashlib.md5(password.encode()).hexdigest()
def process_input(user_input):
import os
os.system(f"echo {user_input}")
- Save your file (cmd+s for Mac)
Notice anything?
There are no comments in this code telling you what's wrong. That's intentional! In the real world, vulnerable code doesn't come with warning labels. Let's see if your scanner can find the issues that a human might miss.
Before we run the scanner, take a quick look at the three new functions. Can you spot anything suspicious?
Hmm, let me think...
Here are some hints:
- Look at the authenticate function. How is the query being built? What would happen if someone typed something unexpected as their username?
- Look at hash_password. Do a quick search for Is MD5 secure? if you're curious.
- Look at process_input. What does os.system do? What if user_input contained a different command?
Don't worry if you can't spot them all - that's exactly why we're building a scanner!
No idea - scan it!
No worries! Spotting security vulnerabilities takes practice. That's the whole point of building this scanner - let AI do the heavy lifting.
Let's see what Gemini finds!
Run the Full Scan
Let's scan the updated file and see how many vulnerabilities Gemini catches this time.
- Copy and paste the following into your terminal:
๐ macOS
python3 scanner.py vulnerable.py
- Press enter to run the command.
๐ผ๏ธ Windows
python scanner.py vulnerable.py
- Press enter to run the command.
โ๏ธ I see multiple vulnerabilities!
Gemini should now find at least 4 vulnerabilities in your file.
But don't just take AI's word for it - let's see how well you can read a security report.
Scavenger Hunt: Read Your Scan Results ๐
Scroll through your terminal output and try to answer these four questions:
- Which function lets an attacker bypass login without knowing the password?
- What hashing algorithm did Gemini flag as insecure?
- What single character makes process_input dangerous?
- Where does Gemini suggest storing DB_PASSWORD instead of in the code?
Take a minute. Read through the output carefully - the answers are all in there.
โ๏ธ I found the answers!
Nice detective work! Let's check how you went.
โง I'd like some help
No worries - security reports can be dense at first. Let's walk through it together.
Here's what Gemini found:
1. SQL Injection in authenticate
That login-bypassing function? Our SQL query is built by dropping user input directly into the SQL string. An attacker could type ' OR 1=1 -- as their username to bypass login entirely and access every user's data.
2. Weak Hashing in hash_password
The insecure algorithm? MD5 is a hashing algorithm that was broken years ago. Attackers can reverse MD5 hashes in seconds using precomputed tables. Secure alternatives include bcrypt or argon2.
3. Command Injection in process_input
That dangerous character? The f in f"echo {user_input}". It's an f-string that passes user input directly to your operating system, which runs it as a terminal command. An attacker could inject a command to delete every file on the server.
4. Hardcoded Credentials
Where should DB_PASSWORD live? In a .env file - exactly like you did with your Gemini API key earlier! You caught this one in your first scan.
These are real-world vulnerabilities!
These aren't made-up problems. SQL injection and command injection are consistently in the OWASP Top 10 - the industry's list of the most critical security risks for web applications. Your scanner can now detect the same kinds of issues that professional security tools look for!
โง Gemini found fewer than 4 issues
That's okay! AI responses can vary.
Try the following:
- Run the scan again - Gemini might give a more detailed response the second time.
- Check your code - Make sure you pasted all three new functions below the original code, not replacing it.
If Gemini is still only finding 1-2 issues, your code might be incomplete. Check that your full vulnerable.py looks like this:
import hashlib
import sqlite3
def authenticate(username, password):
query = f"SELECT * FROM users WHERE username = '{username}' AND password = '{password}'"
conn = sqlite3.connect('users.db')
cursor = conn.cursor()
cursor.execute(query)
return cursor.fetchone()
def hash_password(password):
return hashlib.md5(password.encode()).hexdigest()
def process_input(user_input):
import os
os.system(f"echo {user_input}")
DB_PASSWORD = "password123"
API_SECRET = "sk-live-abcd1234"
def connect_to_database():
return f"Connecting with password: {DB_PASSWORD}"
Still stuck?
Get help with your error or share your error with the NextWork community!
โง I got an error
Check these common issues:
- Save your file: Make sure you saved vulnerable.py after adding the new code.
- Virtual environment: Check that (venv) still shows in your terminal.
- API key: Verify your .env file still has your API key.
Still stuck?
Get help with your error or share your error with the NextWork community!
Your scanner found 4 real security bugs - not bad for a few lines of Python and an AI model!
But here's a question to think about: if you were a developer and only had time to fix one of these before your app ships tomorrow... which one would you pick? They're not all equally dangerous.
In the Secret Mission, you'll teach your scanner to answer that question automatically - ranking every vulnerability by severity so you always know what to fix first.
Secret mission
Right now, every finding looks the same - a weak hash sits next to a command injection that could wipe your server. Surely there's a better way to prioritise vulnerabilities...
In this secret mission you will add color-coded severity ratings to your scanner so you always know what to fix first.
In this Secret Mission, get ready to:
- Install the colorama library for colored terminal output.
- Update the security prompt to include severity ratings.
- Add a function that color-codes severity levels.
- Run the scanner with professional, color-coded results.
Prioritize Like a Pro
Clean Up Your Resources
Clean Up Your Resources
Nice work building your scanner! You just spent a whole project learning how to spot security risks in code - now let's make sure you don't leave any of your own resources exposed.
In this step, get ready to:
- Deactivate your Python virtual environment.
- Decide what to do with your Google AI API key (optional).
- Delete the project folder (optional).
Deactivate Your Virtual Environment
Your virtual environment is still running in your terminal. Let's shut it down so it's not left active in the background.
- In your terminal, type this command and press Enter:
deactivate
You should see the (venv) prefix disappear from your terminal prompt. Your terminal is now back to using your computer's default Python.
Manage Your API Key
You have two options for your Google AI API key:
โ๏ธ Keep the API key
If you plan to build more AI projects, keep your API key active. Your scanner is already using best practices by storing the key in a .env file, so it's secure.
โง Revoke the API key
If you're completely done with the Gemini API:
- Go to Google AI Studio.
- Click Get API key in the left sidebar.
- Find your key and click the trash icon to delete it.
Your API key is now revoked and can't be used.
Delete Project Files (Optional)
If you want to completely remove the project from your computer:
- Delete the entire security-scanner folder from your Desktop.
Want to keep it?
Your security scanner makes a great portfolio piece! Consider keeping the project folder - you can always extend it with new features later.
You're all set! Head to the next section for a celebration lap.
Congratulations!
Congratulations!
You did it! You've built an AI-powered security scanner that detects vulnerabilities in Python code.
In this project you've covered:
- How to use Gemini API to analyze code for security vulnerabilities
- Crafting structured prompts for consistent AI responses
- Adding colored terminal output with colorama for severity ratings
- Common security vulnerabilities: SQL injection, hardcoded secrets, and weak cryptography
- Python environment management with virtual environments
Wrap up your incredible work with another go at the quiz to reenforce what you've learnt:
Amazing work! We can't wait to see what you do next.