惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Martin Fowler
Martin Fowler
V
Visual Studio Blog
有赞技术团队
有赞技术团队
T
Tailwind CSS Blog
B
Blog
I
InfoQ
博客园 - 三生石上(FineUI控件)
阮一峰的网络日志
阮一峰的网络日志
F
Fortinet All Blogs
H
Help Net Security
博客园 - Franky
宝玉的分享
宝玉的分享
博客园 - 司徒正美
C
Check Point Blog
G
Google Developers Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Jina AI
Jina AI
T
The Blog of Author Tim Ferriss
MongoDB | Blog
MongoDB | Blog
云风的 BLOG
云风的 BLOG
A
About on SuperTechFans
罗磊的独立博客
大猫的无限游戏
大猫的无限游戏
IT之家
IT之家

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
DevSecOps in Practice: Tools That Actually Catch Vulnerab...
Hariharan · 2026-04-26 · via DEV Community

Hariharan

Part 1 covered secret scanning with Gitleaks — catching credentials before they reach the repo. That's one layer. But credentials aren't the only problem in app.py. There's a SQL injection vulnerability, an eval() call that lets an attacker run arbitrary Python code, and debug mode left on. None of those are secrets. Gitleaks won't touch them.
That's what SAST is for.

Code repo: https://github.com/pkkht/devsecops-demo/

What SAST is

SAST stands for Static Application Security Testing. It analyses your source code without running it, looking for patterns that indicate security vulnerabilities. No server needed, no database, no HTTP requests — just the code itself.
The key difference from a linter: SAST is specifically looking for security issues, not style or correctness. It knows what SQL injection looks like. It knows which Python functions are dangerous. It knows that debug=True in a Flask app exposes the Werkzeug interactive debugger to anyone who can reach it.

The tool: Bandit

Bandit is the standard SAST tool for Python.
It is open source, maintained by the Python Security community, and maps its findings to CWE (Common Weakness Enumeration) IDs so you know exactly what class of vulnerability you're dealing with.

Installing Bandit

pip install bandit
bandit --version

Enter fullscreen mode Exit fullscreen mode

Running it against the app

bandit -r app.py

Enter fullscreen mode Exit fullscreen mode

The -r flag means recursive — scan the directory, not just a single file. Here it's running against app.py directly.

The first findings are hardcoded passwords — supersecretkey123, the API token, the AWS keys. These are all flagged as B105: hardcoded_password_string, severity Low. Bandit and Gitleaks overlap here — both tools catch hardcoded credentials, just from different angles.

The more serious findings:

B608: hardcoded_sql_expressions — the f-string SQL query on line 69. Severity Medium. This is the SQL injection vulnerability — user input is
embedded directly into the query string.

B307: blacklist — the eval() call on line 127. Severity Medium,
Confidence High. Bandit flags eval() as blacklisted because it executes
arbitrary code. An attacker who can reach the /calculate endpoint can run anything on the server.

B201: flask_debug_truedebug=True on line 137. Severity High.
The Werkzeug debugger is interactive — if an unhandled exception hits in
production, anyone who sees the error page gets a Python shell.

B104: hardcoded_bind_all_interfaces — host="0.0.0.0" on line 137.
Severity Medium. The app is listening on every network interface, not just
localhost.

The summary: 81 lines of code, 8 issues total — 4 Low, 3 Medium, 1 High.

Filtering by severity
In a real pipeline you don't want to fail on every Low finding — you'd never ship anything. The practical approach is to gate on High severity only, and report everything else for visibility.

bandit -r app.py --severity-level high

Enter fullscreen mode Exit fullscreen mode

With --severity-level high, only one finding comes through: the Flask
debug=True. That's the gate. Everything else is still visible in the full report but won't block the build.

Generating a JSON report

bandit -r app.py -f json -o bandit-report.json

Enter fullscreen mode Exit fullscreen mode

The JSON output is what the pipeline uses — it's machine-readable and can be uploaded as a build artifact. One thing to watch: the report contains the actual secret values from the code as context snippets. Add it to .gitignore so it doesn't get committed.

GitHub Actions workflow
Create .github/workflows/sast.yml:

name: SAST - Bandit

on:
  push:
    branches: ["**"]
  pull_request:
    branches: ["**"]

jobs:
  bandit:
    name: Bandit SAST Scan
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'

      - name: Install Bandit
        run: pip install bandit

      - name: Run Bandit
        run: bandit -r app.py --severity-level high -f json -o bandit-report.json

      - name: Upload Bandit Report
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: bandit-report
          path: bandit-report.json

Enter fullscreen mode Exit fullscreen mode

if: always() on the upload step is important — it means the report gets
uploaded even when the scan fails, so you can inspect the findings.
Push it and watch it run:

The pipeline fails. This is the right outcome — Bandit found a HIGH severity issue (debug=True) and exited with code 1. The bandit-report artifact is still uploaded and available for download.
This is the pipeline doing its job. In a real codebase, a developer would fix debug=True, push again, and the build would pass. In this demo repo the vulnerability is intentional, so we leave it failing as a demonstration that the gate is real.

What we've built so far
Three layers are now in place:

  • Gitleaks pre-commit hook — blocks secrets at commit time
  • Gitleaks GitHub Actions — catches secrets at push time
  • Bandit GitHub Actions — catches code vulnerabilities at push time, gates on HIGH severity