Audit with Safety and Semgrep
Learn to audit Python code with Safety and Semgrep, covering dependency checks, static analysis, and integrating them into your workflow.
Focus: audit your code with safety and semgrep
You've written clean code, reviewed it with a teammate, and shipped it to production. Then a vulnerability scanner flags a dependency with a critical CVE, and a static analysis tool finds a SQL injection you never saw. Panic? Not if you've already built auditing into your workflow. In this lesson, you'll learn how to audit your code with safety and semgrep — two tools that catch security issues before they hit production. By the end, you'll know how to check dependencies for known vulnerabilities and scan your Python code for dangerous patterns, so you can ship with confidence.
The problem this lesson solves
Modern software isn't just the code you write — it's the dozens of dependencies you pull in, the frameworks you build on, and the patterns you unknowingly copy from blog posts. Each of those layers is an attack surface. A single outdated package can expose your app to a known remote code execution flaw. A common mistake like concatenating SQL strings can open the door to data theft.
Manual code review can't catch everything. You can't memorize every CVE or spot every insecure pattern in hours of scrolling. That's why you need automated auditing. Tools like Safety and Semgrep act as your security net, catching issues that human eyes often miss.
Pro tip: The best time to audit is before you commit. Make it a habit, not an afterthought.
Core concept / mental model
Think of auditing your code as a two-layer inspection, like a building's security system.
- Safety is the perimeter guard. It checks the packages you depend on against a database of known vulnerabilities. It tells you: "This version of
requestshas a CVE. Update it." - Semgrep is the internal security patrol. It scans your source code for patterns that indicate unsafe practices — like using
eval()with user input, or hardcoded secrets. It finds vulnerabilities in your code, not just your dependencies.
Together, they cover the two main dimensions of security: what you use and what you write.
Definitions: - CVE: Common Vulnerabilities and Exposures — a public list of known security flaws. - Static Analysis: Scanning code without running it, looking for dangerous syntax or patterns. - SAST: Static Application Security Testing — a broader term for tools like Semgrep.
How it works step by step
Here's the mental model for integrating auditing into your workflow:
- Start with dependencies: Run
safety checkto see if any of your installed packages have known vulnerabilities. - Scan your own code: Run
semgrep scanwith built-in or custom rules to find unsafe patterns. - Review the findings: Prioritize based on severity — a critical CVE in a core dependency beats a low-priority style issue.
- Fix and re-audit: Update packages, rewrite risky code, and re-run both tools to confirm the issues are gone.
- Automate: Add these scans to your CI/CD pipeline so every commit gets checked automatically.
Pro tip: Start with the defaults. Safety's free database and Semgrep's default rule set are enough to catch the most obvious issues.
Hands-on walkthrough
Let's get your hands dirty. Install both tools first:
pip install safety semgrep
Expected output: successful installation messages (no errors).
First, audit your dependencies. safety checks against a public vulnerability database. The simplest command is:
safety check
This compares all installed packages against the Safety database. If a vulnerability is found, you'll see something like:
+==================================================================+
| VULNERABILITIES REPORT |
+==================================================================+
| Report generated by Safety CLI 2.3.4 |
| Report generated at: 2025-01-01 12:00:00 |
| Packages scanned: 285 |
| Vulnerabilities found: 2 |
+==================================================================+
| Name | Version | CVE | Severity | |
|------------|---------|------------------------|----------|----------|
| requests | 2.25.1 | CVE-2023-32681 | HIGH | |
| django | 3.2.18 | CVE-2024-27351 | MEDIUM | |
+==================================================================+
Now for Semgrep. Let's create a small vulnerable Python file and scan it.
# vulnerable.py
def get_user_data(user_id):
import sqlite3
conn = sqlite3.connect('app.db')
query = "SELECT * FROM users WHERE id = " + user_id # SQL injection!
return conn.execute(query).fetchall()
def run_calc(expression):
return eval(expression) # Dangerous eval!
Save this as vulnerable.py, then run:
semgrep scan --config=auto
Semgrep will analyze the file and flag the unsafe patterns. Expected output includes a rule like python.lang.security.audit.dangerous-eval.dangerous-eval for the eval() call, and possibly a SQL injection rule. The output will show the line numbers and a suggested fix.
Pro tip: For a quick scan of just one file, use
semgrep scan --config=auto vulnerable.pyto save time.
Compare options / when to choose what
Both tools have alternatives, and you should know what else exists.
| Tool | Scope | Setup | Use case |
|---|---|---|---|
| Safety | Dependencies | Easy (CLI) | Checking installed packages for CVEs |
| pip-audit | Dependencies | Easy (CLI) | Same as Safety, but uses OSV database |
| Bandit | Python code | Easy (CLI) | Security linter for Python |
| Semgrep | Multi-language | Moderate (CLI + rules) | Pattern-based scanning across languages |
| Snyk | Deps + code | Full platform | Full-featured security monitoring |
When should you choose what?
- Use Safety or pip-audit for a quick dependency check in a development environment.
- Use Bandit for a lightweight, Python-only SAST scan.
- Use Semgrep when you need powerful, customizable pattern matching, and you work in multiple languages.
- Use Snyk for a commercial-grade, integrated solution with a dashboard.
Variations: - pip-audit is a drop-in alternative to Safety that aligns with the OSV schema. - Bandit offers a simple set of Python-based rules without the need for a separate install. - Semgrep supports custom rule writing if the built-in patterns aren't enough.
Troubleshooting & edge cases
Issue: safety check returns "Unable to connect to the vulnerability database."
- Fix: Check your internet connection or proxy settings. If you're in an offline environment, use safety check --offline with a manually updated database.
Issue: Semgrep does not find any issues in code that you know is vulnerable.
- Fix: The default rules may not cover every pattern. Try using semgrep scan --config=auto (which enables more rules) or write a custom rule. For SQL injection, ensure your code uses string concatenation — Semgrep's rules often detect that pattern.
Issue: False positives — Safety flags a vulnerability that has no known exploit.
- Fix: Review the CVE details. If it doesn't apply to how you use the package (e.g., the vulnerable function is not used), you can ignore it with safety filter or add a comment in your CI configuration.
Issue: Semgrep flags a line that is actually safe.
- Fix: Understand the rule. Sometimes the pattern is flagged because a more specific rule didn't match. You can suppress a finding with a # nosemgrep comment.
Pro tip: Always verify findings before dismissing them. Security tools are aids, not infallible.
What you learned & what's next
You've learned how to audit your code with safety and semgrep:
- Safety checks your dependencies for known vulnerabilities.
- Semgrep scans your code for dangerous patterns.
- Both can be run from the command line and integrated into CI.
You completed a hands-on exercise that installed both tools and scanned a vulnerable sample file. You also compared them with alternatives like Bandit and Snyk.
Next step: In the next lesson, you'll move from auditing to prevention — learning how to write secure code from the start. You'll apply these auditing skills as an ongoing habit in your secure development workflow.
Remember: Security is not a one-time task. Make safety and semgrep part of your everyday development process — your future self (and your users) will thank you.
Practice recap
To solidify your skills, create a test file with a few known vulnerabilities (like using os.system with user input) and run semgrep scan --config=auto on it. Then install an older version of a package, like requests==2.25.1, and run safety check to see the CVE report. Practice fixing the issues and re-running the tools to confirm they're resolved.
Common mistakes
- Skipping dependency scans before deploying, leaving known CVEs in production.
- Assuming static analysis tools catch every vulnerability — they only find patterns, not business logic flaws.
- Running
safety checkwithout updating the local vulnerability database, leading to outdated results. - Dismissing Semgrep findings as false positives without investigating, potentially missing real issues.
- Not integrating audits into CI, so security checks are done once and forgotten.
Variations
- Use pip-audit as an alternative to Safety for dependency checks, based on the OSV schema.
- Use Bandit as a lightweight SAST tool specifically for Python, ideal for simple projects.
- Write custom Semgrep rules for your project's unique patterns that built-in rules may miss.
Real-world use cases
- A financial app scans all dependencies with Safety before each release to ensure no known CVEs in payment libraries.
- A web developer uses Semgrep in CI to automatically block PRs that introduce SQL injection patterns.
- A DevSecOps team runs both Safety and Semgrep as part of a nightly audit to detect outdated packages and insecure code patterns.
Key takeaways
- Safety audits installed dependencies against known vulnerabilities (CVEs).
- Semgrep performs static analysis to find unsafe code patterns like eval, SQL injection, hardcoded secrets.
- Run safety and semgrep locally before pushing code, and integrate them into CI for continuous auditing.
- Use alternatives like pip-audit, Bandit, or Snyk based on your project's specific needs.
- Audit findings are a starting point; always review and verify before fixing.
- Future lessons will build on auditing to teach proactive secure coding practices.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.