# SECURE: Flask app with proper escaping and templates
from flask import Flask, request, make_response, session, render_template, jsonify
from markupsafe import escape, Markup
import json
import re
from datetime import datetime
app = Flask(__name__)
app.secret_key = 'your-secret-key'
# Input validation functions
def validate_user_id(user_id):
"""Validate user ID format"""
return re.match(r'^[0-9]+$', user_id) is not None
def validate_username(username):
"""Validate username format"""
return re.match(r'^[a-zA-Z0-9_]{3,30}$', username) is not None
def validate_theme(theme):
"""Validate theme against whitelist"""
allowed_themes = ['default', 'dark', 'light', 'blue', 'green']
return theme if theme in allowed_themes else 'default'
def sanitize_room_name(room_name):
"""Sanitize room name"""
# Remove HTML tags and limit length
clean_name = re.sub(r'<[^>]+>', '', room_name)
return clean_name[:50] if clean_name else 'Unnamed Room'
@app.route('/dashboard/<user_id>')
def dashboard(user_id):
"""Secure user dashboard"""
# Validate user ID
if not validate_user_id(user_id):
return render_template('error.html',
message='Invalid user ID format'), 400
username = request.args.get('name', 'User')
theme = validate_theme(request.args.get('theme', 'default'))
welcome_msg = request.args.get('msg', '')
# Validate username
if not validate_username(username):
username = 'User'
# Limit message length
if len(welcome_msg) > 200:
welcome_msg = welcome_msg[:200]
# Use template with auto-escaping
return render_template('dashboard.html',
username=username,
user_id=user_id,
theme=theme,
welcome_msg=welcome_msg)
# templates/dashboard.html
"""
<!DOCTYPE html>
<html>
<head>
<title>Dashboard - {{ username }}</title>
<link rel="stylesheet" href="/static/css/{{ theme }}.css">
</head>
<body class="theme-{{ theme }}">
<h1>Welcome {{ username }}!</h1>
<div id="user-id" data-user="{{ user_id }}">User ID: {{ user_id }}</div>
{% if welcome_msg %}
<div class="welcome-message">{{ welcome_msg }}</div>
{% endif %}
<div id="content">
<p>Your personalized dashboard</p>
</div>
<script>
// Safe JavaScript variables using JSON encoding
var currentUser = {{ username|tojson }};
var userId = {{ user_id|tojson }};
</script>
</body>
</html>
"""
@app.route('/preview', methods=['POST'])
def preview_content():
"""Secure content preview"""
title = request.form.get('title', '').strip()
content = request.form.get('content', '').strip()
author = request.form.get('author', 'Anonymous').strip()
tags = request.form.get('tags', '').strip()
# Validate input lengths
if len(title) > 200 or len(content) > 5000 or len(author) > 50 or len(tags) > 200:
return jsonify({'error': 'Input too long'}), 400
# For API requests, return JSON
if request.headers.get('Accept') == 'application/json':
return jsonify({
'preview': {
'title': title,
'content': content,
'author': author,
'tags': tags,
'date': datetime.now().isoformat()
}
})
# Use template for HTML preview
return render_template('content_preview.html',
title=title,
content=content,
author=author,
tags=tags,
date=datetime.now().strftime('%Y-%m-%d'))
# templates/content_preview.html
"""
<div class="preview-container">
<article class="content-preview">
<h1>{{ title }}</h1>
<div class="meta">
<span class="author">By: {{ author }}</span>
<span class="tags">Tags: {{ tags }}</span>
<span class="date">{{ date }}</span>
</div>
<div class="content">
{{ content|nl2br }}
</div>
</article>
<div class="preview-actions">
<button onclick="publishContent()">Publish</button>
<button onclick="editContent()">Edit</button>
</div>
</div>
<script>
function publishContent() {
alert('Publishing: ' + {{ title|tojson }});
}
</script>
"""
@app.route('/chat/<room_id>')
def chat_room(room_id):
"""Secure chat room"""
# Validate room ID
if not re.match(r'^[a-zA-Z0-9_-]{1,20}$', room_id):
return render_template('error.html',
message='Invalid room ID'), 400
username = session.get('username', 'Anonymous')
last_message = request.args.get('last_msg', '')
room_name = request.args.get('room_name', f'Room {room_id}')
# Validate and sanitize inputs
if not validate_username(username):
username = 'Anonymous'
clean_room_name = sanitize_room_name(room_name)
# Limit last message length
if len(last_message) > 500:
last_message = last_message[:500]
return render_template('chat_room.html',
room_id=room_id,
room_name=clean_room_name,
username=username,
last_message=last_message)
# templates/chat_room.html
"""
<div class="chat-container">
<div class="chat-header">
<h2>{{ room_name }}</h2>
<span class="user-info">Logged in as: {{ username }}</span>
</div>
<div class="chat-messages" id="messages">
{% if last_message %}
<div class="last-message">Last: {{ last_message }}</div>
{% endif %}
</div>
<div class="chat-input">
<input type="text" id="messageInput" placeholder="Type a message...">
<button onclick="sendMessage()">Send</button>
</div>
</div>
<script>
// Safe JavaScript variables
var roomId = {{ room_id|tojson }};
var currentUser = {{ username|tojson }};
var roomName = {{ room_name|tojson }};
function sendMessage() {
var message = document.getElementById('messageInput').value;
// Send message logic here
}
</script>
"""
@app.route('/error')
def error_page():
"""Secure error page"""
error_type = request.args.get('type', 'Unknown')
error_message = request.args.get('msg', 'An error occurred')
# Sanitize error inputs
safe_error_type = re.sub(r'[^a-zA-Z0-9s]', '', error_type)[:50]
safe_error_message = error_message[:200] if error_message else 'An error occurred'
return render_template('error.html',
error_type=safe_error_type,
error_message=safe_error_message,
timestamp=datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
# templates/error.html
"""
<div class="error-page">
<h1>Error: {{ error_type }}</h1>
<div class="error-details">
<p>Message: {{ error_message }}</p>
<p>Timestamp: {{ timestamp }}</p>
</div>
<div class="error-actions">
<a href="/">Go Home</a>
</div>
</div>
"""
@app.route('/api/format', methods=['POST'])
def format_data():
"""Secure API endpoint for formatted content"""
data = request.get_json() or {}
format_type = data.get('format', 'html')
content = data.get('content', '')
title = data.get('title', 'Untitled')
# Validate inputs
if len(content) > 10000 or len(title) > 200:
return jsonify({'error': 'Input too long'}), 400
if format_type == 'html':
# Use template for safe HTML generation
html_content = render_template('formatted_content.html',
title=title,
content=content)
response = make_response(html_content)
response.headers['Content-Type'] = 'text/html; charset=utf-8'
return response
elif format_type == 'json':
return jsonify({
'title': title,
'content': content,
'formatted_at': datetime.now().isoformat()
})
return jsonify({'error': 'Unsupported format'}), 400
# templates/formatted_content.html
"""
<div class="formatted-content">
<h2>{{ title }}</h2>
<div class="content">
{{ content|nl2br }}
</div>
</div>
"""
# Alternative: Manual escaping when make_response is absolutely necessary
@app.route('/legacy/user/<user_id>')
def legacy_user_page(user_id):
"""Legacy endpoint requiring make_response with manual escaping"""
if not validate_user_id(user_id):
return make_response(escape('<p>Invalid user ID</p>'), 400)
username = request.args.get('name', 'User')
# Manual escaping
safe_user_id = escape(user_id)
safe_username = escape(username)
html_content = f"""
<html>
<head>
<title>User {safe_user_id}</title>
</head>
<body>
<h1>Welcome {safe_username}!</h1>
<p>User ID: {safe_user_id}</p>
</body>
</html>
"""
response = make_response(html_content)
response.headers['Content-Type'] = 'text/html; charset=utf-8'
return response
if __name__ == '__main__':
app.run(debug=False) # Never run with debug=True in production