Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions .github/workflows/pull-request-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# This workflow check each pull request, overall redundant, but might be useful in some cases

name: Pull request unittest security check

on:
pull_request:
branches: [ main ]

env:
API_KEY: ${{ secrets.API_KEY }}

jobs:
lint:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- name: Set up Python 3.x
uses: actions/setup-python@v4
with:
python-version: '3.13'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements-lint.txt
- name: Run ruff check
run: ruff check .
- name: Run black check
run: black --check .
- name: Run isort check
run: isort --check .

test:
runs-on: ubuntu-latest
needs: lint

strategy:
matrix:
os: [ ubuntu-latest, macos-latest, windows-latest ]
python-version: [ 3.9, 3.13 ]

steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Display Python version
run: python -c "import sys; print(sys.version)"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pytest pytest-cov
- name: Run test suite
run: |
pytest --cov=api tests/

bandit-scan:
runs-on: ubuntu-latest
needs: test
steps:
- uses: actions/checkout@v3
# Runs a pre configured Bandit scan
- name: Run bandit
uses: jpetrucciani/bandit-check@master
with:
# only scans under this path
path: './api'
92 changes: 92 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# This workflow will install Python dependencies, lint and run test suite for all branches
# And bandit security checks for master branch

name: Lint and test

on:
push:


env:
API_KEY: ${{ secrets.API_KEY }}

jobs:
lint:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- name: Set up Python 3.x
uses: actions/setup-python@v4
with:
python-version: '3.13'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements-lint.txt
- name: Run ruff check
run: ruff check .
- name: Run black check
run: black --check .
- name: Run isort check
run: isort --check .

test:
if: ${{ !(github.ref == 'refs/heads/main') }}
runs-on: ubuntu-latest
needs: lint

steps:
- uses: actions/checkout@v3
- name: Set up Python 3.x
uses: actions/setup-python@v4
with:
python-version: '3.13'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pytest pytest-cov
- name: Run test suite
run: |
pytest --cov=api tests/

test_main:
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
needs: lint

strategy:
matrix:
os: [ ubuntu-latest, macos-latest, windows-latest ]
python-version: [ 3.9, 3.11, 3.13 ]

steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Display Python version
run: python -c "import sys; print(sys.version)"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pytest pytest-cov
- name: Run test suite
run: |
pytest --cov=api tests/

bandit-scan:
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
needs: test_main
steps:
- uses: actions/checkout@v3
# Runs a pre configured Bandit scan
- name: Run bandit
uses: jpetrucciani/bandit-check@master
with:
# only scans under this path
path: './api'
5 changes: 3 additions & 2 deletions api/app.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import datetime
import os
import random
import secrets
import uuid

from flask import Flask, jsonify, request, send_from_directory
Expand Down Expand Up @@ -36,7 +37,7 @@ def home():

@app.route("/quote", methods=["GET"])
def get_random_quote():
return jsonify(random.choice(QUOTES))
return jsonify(secrets.choice(QUOTES))


@app.route("/paths", methods=["GET"])
Expand Down Expand Up @@ -126,7 +127,7 @@ def create_roadmap():
(i, random.sample(DEV_PATHS[i]["tips"], min(2, len(DEV_PATHS[i]["tips"])))) for i in interests
]
},
"quote": random.choice(QUOTES),
"quote": secrets.choice(QUOTES),
}

# Store in our simulated database
Expand Down
16 changes: 8 additions & 8 deletions api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ def api_request(method, endpoint, data=None):
url = f"{request.url_root}{endpoint}"

if method.lower() == "get":
response = requests.get(url, headers=headers)
response = requests.get(url, headers=headers, timeout=5)
elif method.lower() == "post":
response = requests.post(url, headers=headers, json=data)
response = requests.post(url, headers=headers, json=data, timeout=5)
elif method.lower() == "put":
response = requests.put(url, headers=headers, json=data)
response = requests.put(url, headers=headers, json=data, timeout=5)
else:
raise ValueError(f"Unsupported method: {method}")

Expand All @@ -25,18 +25,18 @@ def api_request(method, endpoint, data=None):

@bp.route("/home", methods=["GET"])
def home():
quote_response = requests.get(f"{request.url_root}quote")
quote_response = requests.get(f"{request.url_root}quote", timeout=5)
quote = quote_response.json() if quote_response.ok else {"text": "Loading failed", "author": "System"}

return render_template("home.html", quote=quote)


@bp.route("/dashboard", methods=["GET"])
def dashboard():
quote_response = requests.get(f"{request.url_root}quote")
quote_response = requests.get(f"{request.url_root}quote", timeout=5)
quote = quote_response.json() if quote_response.ok else {"text": "Loading failed", "author": "System"}

paths_response = requests.get(f"{request.url_root}paths")
paths_response = requests.get(f"{request.url_root}paths", timeout=5)
paths = paths_response.json()["available_paths"] if paths_response.ok else []

user_roadmaps = session.get("user_roadmaps", [])
Expand All @@ -49,7 +49,7 @@ def dashboard():
roadmaps_data.append(response.json())
except Exception:
# Log the error in a production app
pass
roadmaps_data = []

return render_template("dashboard.html", quote=quote, paths=paths, roadmaps=roadmaps_data)

Expand Down Expand Up @@ -94,7 +94,7 @@ def create_roadmap():

return redirect(url_for("views.create_roadmap"))

paths_response = requests.get(f"{request.url_root}paths")
paths_response = requests.get(f"{request.url_root}paths", timeout=5)
paths = paths_response.json()["available_paths"] if paths_response.ok else []

return render_template("create_roadmap.html", paths=paths)
Expand Down