Building a DevSecOps Pipeline for Flask application

In this article, we will build a DevSecOps CI pipeline for a flask application.
We will use a very simple flask application in this article and we will host the application using Gunicorn.
Below is the code for the flask application:
https://github.com/lalith2211/DevSecOps-flask
app.py:
# app.py
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/')
def home():
return jsonify({"message": "Welcome to the Flask DevSecOps App!"})
@app.route('/health')
def health():
return jsonify({"status": "healthy"}), 200
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
requirements.txt:
Flask
gunicorn
gunicorn_config.py:
import os
workers = int(os.environ.get('GUNICORN_PROCESSES', '2'))
threads = int(os.environ.get('GUNICORN_THREADS', '4'))
bind = os.environ.get('GUNICORN_BIND', '0.0.0.0:8080')
forwarded_allow_ips = '*'
secure_scheme_headers = { 'X-Forwarded-Proto': 'https' }
dockerfile:
FROM python:3.10:slim
WORKDIR /app
COPY app.py requirements.txt gunicorn_config.py /app/
RUN pip install -r requirements.txt
EXPOSE 5000
CMD ["gunicorn","-b","0.0.0.0:5000","app:app"]
Build docker using the below command test it:
docker build -t sailalithv/flask-devsecops-app .
docker run -b 5000:5000 sailalithv/flask-devsecops-app
Great! Now that we are done with our application and dockerfile. Let’s push the dockerfile to docker hub:
docker login
docker push sailalithv/flask-devsecops-app
Now, lets get started with the github actions workflow:
name: DevSecOps Pipeline for Flask Application (Gunicorn)
on:
push:
branches:
- main
jobs:
build:
runs-on: Ubuntu-latest
steps:
- name: Setup Python
uses: actions/setup-python@v5.4.0
with:
python-version: '3.10'
- name: Checkout
uses: actions/checkout@v4.2.2
- name: install requirements
run: |
pip install -r requirements.txt
pip install flake8 bandit safety
- name: code linting
run: flake8 app.py
# Bandit scans Python code for common security issues like insecure use of eval().
- name: Bandit Scan
run: bandit -r .
# using Safety - for checking the vulnerabilites in the packages
- name: safety package auditing
run: safety check --file=requirements.txt
- name: Gitleaks
uses: gitleaks/gitleaks-action@v2.3.4
- name: Docker Login
uses: docker/login-action@v3.3.0
with:
username: sailalithdevops
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Docker Build and push
uses: docker/build-push-action@v6
with:
push: true
tags: sailalithdevops/flask-devsecops-app:${{ github.run_id }}
provenance: true
sbom: true
This code only consists of pushing the build image to docker registry.
some sample outputs if something goes wrong in the CI steps:
Bandit error:
[main] INFO profile include tests: None
[main] INFO profile exclude tests: None
[main] INFO cli include tests: None
[main] INFO cli exclude tests: None
[main] INFO running on Python 3.10.16
Run started:2025-03-06 13:35:23.472408
Test results:
>> Issue: [B104:hardcoded_bind_all_interfaces] Possible binding to all interfaces.
Severity: Medium Confidence: Medium
CWE: CWE-605 (https://cwe.mitre.org/data/definitions/605.html)
More Info: https://bandit.readthedocs.io/en/1.8.3/plugins/b104_hardcoded_bind_all_interfaces.html
Location: ./app.py:17:17
16 if __name__ == '__main__':
17 app.run(host='0.0.0.0', port=5000)
--------------------------------------------------
Code scanned:
Total lines of code: 16
Total lines skipped (#nosec): 0
Total potential issues skipped due to specifically being disabled (e.g., #nosec BXXX): 0
Run metrics:
Total issues (by severity):
Undefined: 0
Low: 0
Medium: 1
High: 0
Total issues (by confidence):
Undefined: 0
Low: 0
Medium: 1
High: 0
Files skipped (0):
Error: Process completed with exit code 1.
Subscribe to my newsletter
Read articles from Sai Lalith Voopalanchi directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
