diff --git a/jobfinder/__pycache__/app.cpython-311.pyc b/jobfinder/__pycache__/app.cpython-311.pyc new file mode 100644 index 0000000..6301d91 Binary files /dev/null and b/jobfinder/__pycache__/app.cpython-311.pyc differ diff --git a/jobfinder/__pycache__/app.cpython-312.pyc b/jobfinder/__pycache__/app.cpython-312.pyc new file mode 100644 index 0000000..3b21188 Binary files /dev/null and b/jobfinder/__pycache__/app.cpython-312.pyc differ diff --git a/jobfinder/__pycache__/db.cpython-311.pyc b/jobfinder/__pycache__/db.cpython-311.pyc new file mode 100644 index 0000000..81be20a Binary files /dev/null and b/jobfinder/__pycache__/db.cpython-311.pyc differ diff --git a/jobfinder/__pycache__/models.cpython-311.pyc b/jobfinder/__pycache__/models.cpython-311.pyc new file mode 100644 index 0000000..062beca Binary files /dev/null and b/jobfinder/__pycache__/models.cpython-311.pyc differ diff --git a/jobfinder/__pycache__/models.cpython-312.pyc b/jobfinder/__pycache__/models.cpython-312.pyc index f07d8fe..cec8909 100644 Binary files a/jobfinder/__pycache__/models.cpython-312.pyc and b/jobfinder/__pycache__/models.cpython-312.pyc differ diff --git a/jobfinder/app.py b/jobfinder/app.py index c063b29..953e7d9 100644 --- a/jobfinder/app.py +++ b/jobfinder/app.py @@ -4,6 +4,8 @@ from instance.config import Config from db import db from datetime import datetime +from sqlalchemy import text +import hashlib # Add this import app = Flask(__name__) app.config.from_object(Config) @@ -18,15 +20,27 @@ def home(): @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': - email = request.form['email'] - password = request.form['password'] - - user = User.query.filter_by(email=email).first() - if user and user.password == password: - session['current_user'] = user.id # Store user ID in session + username = request.form['username'] + # Hash the input password for comparison + password = hashlib.md5(request.form['password'].encode()).hexdigest() + + # More vulnerable SQL query implementation + conn = db.engine.raw_connection() + cursor = conn.cursor() + query = f"SELECT * FROM user WHERE username = '{username}' AND password = '{password}'" + cursor.execute(query) + user = cursor.fetchone() + conn.close() + + if user: + # Vulnerable: No session timeout set + session['current_user'] = user[0] # Just stores user ID without any expiration + # Vulnerable: No session regeneration + # Vulnerable: No remember-me token flash("Login successful!") return redirect(url_for('main_page')) else: + # Vulnerable: No login attempt counting flash("Invalid login credentials") return redirect(url_for('login')) @@ -35,16 +49,24 @@ def login(): @app.route('/signup', methods=['GET', 'POST']) def signup(): if request.method == 'POST': + username = request.form['username'] name = request.form['name'] email = request.form['email'] - password = request.form['password'] + password = hashlib.md5(request.form['password'].encode()).hexdigest() + favorite_color = request.form['favorite_color'] - existing_user = User.query.filter_by(email=email).first() + existing_user = User.query.filter_by(username=username).first() if existing_user: - flash('Email already exists!') + flash('Username already exists!') return redirect(url_for('signup')) - new_user = User(name=name, email=email, password=password) + new_user = User( + username=username, + name=name, + email=email, + password=password, + favorite_color=favorite_color + ) db.session.add(new_user) db.session.commit() @@ -76,7 +98,8 @@ def main_page(): @app.route('/logout') def logout(): - session.pop('current_user', None) # Remove the user from the session + # Vulnerable: Doesn't invalidate on server side + session.pop('current_user', None) return redirect(url_for('login')) @app.route('/post_job') @@ -110,6 +133,81 @@ def post_job(): flash('Job posted successfully!') return redirect(url_for('main_page')) +@app.route('/delete_job/', methods=['GET']) +def delete_job(job_id): + # Vulnerable: No authentication check! + job = Job.query.get(job_id) + if job: + db.session.delete(job) + db.session.commit() + flash('Job deleted successfully!') + return redirect(url_for('main_page')) + +@app.route('/admin_panel') +def admin_panel(): + # Vulnerable: No admin check! + users = User.query.all() + jobs = Job.query.all() + return render_template('admin.html', users=users, jobs=jobs) + +@app.route('/delete_user/') +def delete_user(user_id): + # Vulnerable: No authentication check! + user = User.query.get(user_id) + if user: + db.session.delete(user) + db.session.commit() + flash('User deleted successfully!') + return redirect(url_for('admin_panel')) + +@app.route('/forgot_password', methods=['GET', 'POST']) +def forgot_password(): + if request.method == 'POST': + username = request.form['username'] + user = User.query.filter_by(username=username).first() + + if user: + session['reset_user'] = user.username + return redirect(url_for('security_question')) + else: + flash('Username not found') + + return render_template('forgot_password.html') + +@app.route('/security_question', methods=['GET', 'POST']) +def security_question(): + if 'reset_user' not in session: + return redirect(url_for('forgot_password')) + + if request.method == 'POST': + color = request.form['color'] + user = User.query.filter_by(username=session['reset_user']).first() + + if user and color == user.favorite_color: + return redirect(url_for('reset_password')) + else: + flash('Incorrect answer') + + return render_template('security_question.html') + +@app.route('/reset_password', methods=['GET', 'POST']) +def reset_password(): + if 'reset_user' not in session: + return redirect(url_for('forgot_password')) + + if request.method == 'POST': + new_password = request.form['new_password'] + user = User.query.filter_by(username=session['reset_user']).first() + + if user: + user.password = hashlib.md5(new_password.encode()).hexdigest() + db.session.commit() + session.pop('reset_user', None) + flash('Password reset successful!') + return redirect(url_for('login')) + + return render_template('reset_password.html') + if __name__ == '__main__': with app.app_context(): db.create_all() # Ensure tables exist before running diff --git a/jobfinder/create_db.py b/jobfinder/create_db.py index 8fc63f1..7a41ab4 100644 --- a/jobfinder/create_db.py +++ b/jobfinder/create_db.py @@ -1,11 +1,25 @@ from app import app from db import db from models import User, Job +import hashlib with app.app_context(): + # Drop all tables and recreate them + db.drop_all() db.create_all() + + # Create a test user + test_user = User( + username='test', + name='Test User', + email='test@example.com', + password=hashlib.md5('test123'.encode()).hexdigest(), + favorite_color='blue' + ) + db.session.add(test_user) + db.session.commit() -print("Database created successfully!") +print("Database created successfully with test user!") diff --git a/jobfinder/instance/__pycache__/config.cpython-311.pyc b/jobfinder/instance/__pycache__/config.cpython-311.pyc new file mode 100644 index 0000000..069af89 Binary files /dev/null and b/jobfinder/instance/__pycache__/config.cpython-311.pyc differ diff --git a/jobfinder/instance/__pycache__/config.cpython-312.pyc b/jobfinder/instance/__pycache__/config.cpython-312.pyc index da27320..352fce9 100644 Binary files a/jobfinder/instance/__pycache__/config.cpython-312.pyc and b/jobfinder/instance/__pycache__/config.cpython-312.pyc differ diff --git a/jobfinder/instance/site.db b/jobfinder/instance/site.db index 8f36b1c..fae3ff3 100644 Binary files a/jobfinder/instance/site.db and b/jobfinder/instance/site.db differ diff --git a/jobfinder/models.py b/jobfinder/models.py index 722840c..f6f8f2c 100644 --- a/jobfinder/models.py +++ b/jobfinder/models.py @@ -2,12 +2,14 @@ class User(db.Model): id = db.Column(db.Integer, primary_key=True) - name = db.Column(db.String(100), nullable=False) # Added name field - email = db.Column(db.String(120), unique=True, nullable=False) + username = db.Column(db.String(80), unique=True, nullable=False) password = db.Column(db.String(120), nullable=False) + name = db.Column(db.String(120), nullable=False) + email = db.Column(db.String(120), unique=True, nullable=False) + favorite_color = db.Column(db.String(20), nullable=False) def __repr__(self): - return f'' + return f'' class Job(db.Model): # This defines a "job" table id = db.Column(db.Integer, primary_key=True) diff --git a/jobfinder/static/styles.css b/jobfinder/static/styles.css index 587c5c0..49838ea 100644 --- a/jobfinder/static/styles.css +++ b/jobfinder/static/styles.css @@ -333,4 +333,49 @@ textarea:focus { color: #166534; } +/* Admin panel styles */ +.admin-table { + width: 100%; + border-collapse: collapse; + margin: 20px 0; +} + +.admin-table th, +.admin-table td { + padding: 12px; + text-align: left; + border-bottom: 1px solid var(--gray-light); +} + +.admin-table th { + background-color: var(--primary); + color: white; +} + +.delete-btn { + background-color: #ef4444; + color: white; + padding: 6px 12px; + border-radius: 4px; + text-decoration: none; + font-size: 14px; +} + +.delete-btn:hover { + background-color: #dc2626; +} + +.forgot-password { + display: block; + text-align: right; + font-size: 0.9rem; + color: var(--primary); + text-decoration: none; + margin-top: 0.5rem; +} + +.forgot-password:hover { + color: var(--primary-dark); +} + diff --git a/jobfinder/templates/admin.html b/jobfinder/templates/admin.html new file mode 100644 index 0000000..d76f676 --- /dev/null +++ b/jobfinder/templates/admin.html @@ -0,0 +1,57 @@ + + + + + + Admin Panel - JobFinder + + + + +
+

Admin Panel

+ +

Users

+ + + + + + + + {% for user in users %} + + + + + + + {% endfor %} +
IDUsernameEmailActions
{{ user.id }}{{ user.username }}{{ user.email }} + Delete +
+ +

Jobs

+ + + + + + + + + {% for job in jobs %} + + + + + + + + {% endfor %} +
IDTitleCompanyPosted ByActions
{{ job.id }}{{ job.title }}{{ job.company }}{{ job.posted_by }} + Delete +
+
+ + diff --git a/jobfinder/templates/forgot_password.html b/jobfinder/templates/forgot_password.html new file mode 100644 index 0000000..9d19bf8 --- /dev/null +++ b/jobfinder/templates/forgot_password.html @@ -0,0 +1,40 @@ + + + + + + Forgot Password - JobFinder + + + + +
+
+
+

Forgot Password

+

Enter your username to reset your password

+
+ + {% with messages = get_flashed_messages() %} + {% if messages %} + {% for message in messages %} +
{{ message }}
+ {% endfor %} + {% endif %} + {% endwith %} + +
+
+ + +
+ +
+ + +
+
+ + diff --git a/jobfinder/templates/login.html b/jobfinder/templates/login.html index 2e72951..93849dc 100644 --- a/jobfinder/templates/login.html +++ b/jobfinder/templates/login.html @@ -25,15 +25,16 @@

JobFinder

- - + +
+ Forgot Password?
diff --git a/jobfinder/templates/reset_password.html b/jobfinder/templates/reset_password.html new file mode 100644 index 0000000..fc06a94 --- /dev/null +++ b/jobfinder/templates/reset_password.html @@ -0,0 +1,36 @@ + + + + + + Reset Password - JobFinder + + + + +
+
+
+

Reset Password

+

Enter your new password

+
+ + {% with messages = get_flashed_messages() %} + {% if messages %} + {% for message in messages %} +
{{ message }}
+ {% endfor %} + {% endif %} + {% endwith %} + + +
+ + +
+ + +
+
+ + diff --git a/jobfinder/templates/security_question.html b/jobfinder/templates/security_question.html new file mode 100644 index 0000000..56b83d3 --- /dev/null +++ b/jobfinder/templates/security_question.html @@ -0,0 +1,42 @@ + + + + + + Security Question - JobFinder + + + + +
+
+
+

Security Question

+

What is your favorite color?

+
+ + {% with messages = get_flashed_messages() %} + {% if messages %} + {% for message in messages %} +
{{ message }}
+ {% endfor %} + {% endif %} + {% endwith %} + +
+
+ +
+ +
+
+
+ + diff --git a/jobfinder/templates/signup.html b/jobfinder/templates/signup.html index ea6e351..bcca9f9 100644 --- a/jobfinder/templates/signup.html +++ b/jobfinder/templates/signup.html @@ -3,34 +3,59 @@ - Sign Up + Sign Up - JobFinder + - -
-
-

Welcome to JobFinder

-

Find your dream job today

+ +
+
+
+

JobFinder

+

Create your account

+
+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+ + +
-
- -

Sign Up

- -
-
-

- -
-

- -
-

- - -
- -

Already have an account? Login here

-