'''
The purpose of this program is to demonstrate proper code security practices
and logging methods to secure a python flask web app.
'''
from flask import Flask, render_template, request, jsonify, flash, redirect, url_for
import logging
logging.basicConfig(filename='Troubleshooting_app.log', level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', )
logger = logging.getLogger(__name__)
app = Flask(__name__)
app.secret_key = 'supersecretkeyforflasksessions'
# Equipment Pricing Data
EQUIPMENT_PRICES = {
"Bulldozer": 500,
"Excavator": 450,
"Crane": 800
}
# Helper Functions
def validate_username(username):
assert username is not None, "Username is empty"
assert isinstance(username, str), "Username must be string"
logger.info("Username is valid")
allowed_users = {"admin", "alice", "bob", "charlie"}
if username in allowed_users:
return True
return False
# Routes
@app.route('/')
def home():
logger.info("Home page accessed")
return render_template('index.html')
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form.get("username")
password = request.form.get("password")
# Simulated IP address
ip_address = request.remote_addr
logger.info(f"Login attempt from {username} {ip_address}")
assert ip_address is not None, "IP address is empty"
if (ip_address.startswith("192.168.") or ip_address.startswith("10.")):
pass
else:
logger.warning("IP is suspicious")
# Authentication Logic
if validate_username(username) and password == "secret123":
logger.info(f"User {username} logged in.")
flash(f"Welcome back, {username}!", "success")
return redirect(url_for('home'))
else:
logger.warning(f"Login failed for {username}.")
flash("Invalid credentials.", "danger")
return redirect(url_for('login'))
return render_template('login.html')
@app.route('/rent', methods=['GET', 'POST'])
def rent_equipment():
rental_result = None
if request.method == 'POST':
equipment_type = request.form.get("equipment_type")
days_str = request.form.get("days")
logger.info(f"Rental request of {equipment_type} for {days_str}")
# Potential Crash: equipment_type not in dictionary
try:
if equipment_type not in EQUIPMENT_PRICES:
raise KeyError(f"{equipment_type} is not in dictionary")
daily_rate = EQUIPMENT_PRICES[equipment_type]
# Potential Crash: invalid days_str
days = int(days_str)
# Logic Defect: days <= 0
assert days > 0, "Days must be greater than 0"
logger.info("Days is valid")
total_cost = daily_rate * days
logger.info(f"Calculated cost: {total_cost}")
rental_result = {
"equipment": equipment_type,
"days": days,
"total_cost": total_cost
}
flash("Rental calculated successfully!", "success")
except KeyError:
logger.error(f"{equipment_type} is not in dictionary")
flash("Invalid equipment input")
except ValueError:
logger.warning("Days value is invalid")
flash("Invalid days value")
except AssertionError:
logger.error("Days input is invalid")
flash("Invalid days input")
return render_template('rent.html', rental_result=rental_result)
if __name__ == '__main__':
logger.info("Starting application...")
app.run(debug=False, port=5000)
"""
This file tests the security of the web app by running functions that
check if the web app catches bugs and security vulnerabilities.
"""
import pytest
from app import app, validate_username, EQUIPMENT_PRICES
@pytest.fixture
def client():
app.config['TESTING'] = True
with app.test_client() as client:
yield client
def test_invalid_equipment_type_handled(client):
response = client.post('/rent', data={
'equipment_type': 'SpaceShuttle',
'days': '3'
})
assert response.status_code == 200, \
"Invalid equipment type caused a crash! Implement try/except."
def test_non_numeric_days_handled(client):
response = client.post('/rent', data={
'equipment_type': 'Bulldozer',
'days': 'abc'
})
assert response.status_code == 200, \
"Non-numeric days caused a crash! Implement try/except."
def test_negative_days_rejected(client):
response = client.post('/rent', data={
'equipment_type': 'Bulldozer',
'days': '-5' # Invalid - negative days
})
assert response.status_code == 200
assert b'-2500' not in response.data, \
"Negative days calculated a negative cost! Add days > 0 validation."
def test_input_validation():
assert validate_username("admin") == True
assert validate_username("alice") == True
assert validate_username("bob") == True
assert validate_username("charlie") == True
assert validate_username("hacker") == False
def test_correct_output(client):
response = client.post('/rent', data={
'equipment_type': 'Bulldozer',
'days': '3'
})
assert response.status_code == 200
assert b'1500' in response.data
response = client.post('/rent', data={
'equipment_type': 'Bulldozer',
'days': '5'
})
assert response.status_code == 200
assert b'2250' in response.data
def test_application_behavior(client):
response = client.get('/')
assert response.status_code == 200
response = client.post('/login', data={
'username': 'admin',
'password': 'secret123'
})
assert response.status_code == 302 # Redirect on success