Overview
Mirage implements several content moderation features to ensure a safe and secure platform. These include HTML sanitization, markdown rendering safeguards, character limits, and voting mechanisms.
All user-generated content is automatically sanitized to prevent XSS attacks and malicious code injection.
HTML Sanitization
Mirage uses the Bleach library to sanitize user-generated HTML content, preventing security vulnerabilities.
Safe Markdown Rendering
User content is processed through a sanitization pipeline:
# From app/utils.py:12-16
def safe_markdown ( text ):
"""Convert markdown to sanitized HTML"""
html = markdown.markdown(text)
cleaner = Cleaner( tags = ALLOWED_TAGS , attributes = ALLOWED_ATTRIBUTES )
return cleaner.clean(html)
Markdown conversion
Raw text is converted to HTML using the Python markdown library.
HTML sanitization
The resulting HTML is cleaned to remove any dangerous tags or attributes.
Safe output
Only whitelisted tags and attributes are allowed in the final output.
Only specific HTML tags are permitted in user content (app/config.py:12-16):
ALLOWED_TAGS = [
'a' , 'abbr' , 'acronym' , 'b' , 'blockquote' , 'code' , 'em' ,
'i' , 'li' , 'ol' , 'strong' , 'ul' , 'p' , 'br' , 'img' ,
'h1' , 'h2' , 'h3' , 'h4' , 'pre'
]
Tag Purpose Security Risk aLinks Controlled via href attribute abbr, acronymAbbreviations Safe b, strong, em, iText formatting Safe blockquoteQuotations Safe code, preCode blocks Safe ul, ol, liLists Safe p, brParagraphs and breaks Safe h1-h4Headers Safe imgImages Controlled via src attribute
Allowed HTML Attributes
Even allowed tags have restricted attributes:
ALLOWED_ATTRIBUTES = {
'a' : [ 'href' , 'title' ],
'img' : [ 'src' , 'alt' , 'title' ]
}
Dangerous attributes are blocked , including:
onclick, onload, onerror (JavaScript event handlers)
style with javascript: URLs
Any data attributes that could store malicious code
What Gets Blocked
The sanitizer automatically removes:
Script Tags < script > alert ( 'XSS' ) </ script >
Completely removed from output
Event Handlers < a onclick = " malicious ()" > Click </ a >
onclick attribute stripped
Iframe Embeds < iframe src = "evil.com" ></ iframe >
Entire tag removed
Form Elements < form >< input type = "text" ></ form >
Forms and inputs blocked
Content Length Limits
Mirage enforces character limits on user-generated content to maintain quality and prevent abuse.
Post Character Limit
Posts are limited to 512 characters :
# From app/routes/posts.py:20-21
if len (content) > 512 :
return jsonify({ 'error' : 'post content cannot exceed 512 characters' }), 400
The 512-character limit encourages concise, focused communication similar to microblogging platforms.
Reply Character Limit
Replies to posts also have a 512-character limit :
# From app/routes/posts.py:152-153
if len (content) > 512 :
return jsonify({ 'error' : 'reply content cannot exceed 512 characters' }), 400
Message Length
Room messages don’t have an explicit character limit in the code, but best practices suggest keeping them reasonable.
While messages don’t have a hard limit, extremely long messages may cause performance issues or be truncated by clients.
Voting System
The voting system provides community-driven content moderation.
Upvotes and Downvotes
Users can vote on posts to indicate quality:
# Vote on a post
response = requests.post(
'https://api.mirage.com/api/vote_post' ,
headers = { 'Authorization' : 'your_token' },
json = {
'post_id' : 123 ,
'vote_type' : 'up' # or 'down'
}
)
Voting Rules
Users cannot vote on their own content: # From app/routes/posts.py:97-99
if post_author == username:
conn.close()
return jsonify({ 'error' : 'cannot vote on your own post' }), 403
One vote per user per post
Each user can only vote once on a post. Changing your vote is allowed: # From app/routes/posts.py:102-121
c.execute( 'SELECT vote_type FROM post_votes WHERE post_id=? AND username=?' ,
(post_id, username))
existing_vote = c.fetchone()
if existing_vote:
if existing_vote[ 0 ] == vote_type:
return jsonify({ 'error' : 'already voted this way' }), 400
else :
# Reverse previous vote and apply new one
Total upvotes and downvotes are visible to everyone, but individual votes are private.
Vote Tracking
Votes are stored in the post_votes table:
-- From app/db.py:132-141
CREATE TABLE post_votes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
post_id INTEGER NOT NULL ,
username TEXT NOT NULL ,
vote_type TEXT NOT NULL ,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (post_id) REFERENCES posts(id),
FOREIGN KEY (username) REFERENCES users(username),
UNIQUE (post_id, username)
)
The UNIQUE(post_id, username) constraint ensures one vote per user per post at the database level.
Message Lifecycle Management
Automatic message expiration provides a form of content moderation by limiting message retention.
Automatic Message Expiration
Messages expire after 30 minutes (1800 seconds):
# From app/config.py:6
MESSAGE_LIFESPAN = 60 * 30 * 30 # 30 minutes
# Cleanup in app/routes/chat.py:161-162
now = time.time()
messages[:] = [m for m in messages if now - m[ 'created_at' ] < MESSAGE_LIFESPAN ]
Message Count Limit
Only the most recent 100 messages are retained:
# From app/config.py:5
MAX_MESSAGES = 100
# Cleanup in app/routes/chat.py:163-164
if len (messages) > MAX_MESSAGES :
messages.pop( 0 )
Benefits of ephemeral messages:
Reduces risk of long-term harassment
Encourages real-time engagement
Minimizes storage of potentially problematic content
Provides natural content rotation
User Profile Moderation
Custom CSS Sanitization
Users can add custom CSS to profiles, but this could be a security risk if not handled properly.
# Custom CSS is stored but should be sanitized client-side
response = requests.post(
'https://api.mirage.com/api/user/settings' ,
headers = headers,
json = {
'custom_css' : '.profile { background: blue; }'
}
)
Custom CSS security concern : The current implementation stores custom CSS without server-side validation. Client applications should:
Sanitize CSS before rendering
Use CSP (Content Security Policy) headers
Restrict CSS properties that could be abused
Profile Content Limits
Profile Field Constraints
Field Type Constraints Moderation Username string Unique, required Set at registration Email string Unique, required, private Format validation recommended Description text Optional No length limit (should be added) Avatar URL string URL format Should validate URL format Custom CSS text Optional Security risk - needs sanitization Background Image string URL format Should validate URL format
Authentication and Authorization
Proper authentication is the foundation of content moderation.
Token Validation
Every protected endpoint validates the authentication token:
# From app/routes/chat.py:27-32
c.execute( 'SELECT username FROM users WHERE token=?' , (token,))
user = c.fetchone()
if not user:
conn.close()
return jsonify({ 'error' : "unauthorized" }), 401
Room Membership Checks
Users must be room members to interact with room content:
# From app/routes/chat.py:146-149
c.execute( 'SELECT id FROM room_members WHERE room_id=? AND username=?' ,
(room_id, user[ 0 ]))
if not c.fetchone():
return jsonify({ 'error' : 'you are not in this room' }), 403
Authentication (401) Verifies who you are via token validation
Authorization (403) Verifies what you can do via permission checks
File Upload Moderation
File uploads have specific restrictions for safety.
File Size Limit
Files are limited to 24MB to prevent resource abuse:
# From app/routes/upload.py:31-32
if file .content_length > 24 * 1024 * 1024 :
return jsonify({ 'error' : 'File size exceeds the 24MB limit' }), 400
File Type Handling
Currently, Mirage accepts all file types (Content-Type: application/octet-stream).
Security recommendation : Implement file type validation to restrict uploads to specific categories (documents, images, etc.) and prevent executable files.
Recommended File Type Restrictions
# Suggested implementation (not in current codebase)
ALLOWED_EXTENSIONS = {
'images' : [ 'png' , 'jpg' , 'jpeg' , 'gif' , 'webp' , 'svg' ],
'documents' : [ 'pdf' , 'doc' , 'docx' , 'txt' , 'md' ],
'archives' : [ 'zip' , 'tar' , 'gz' ]
}
BLOCKED_EXTENSIONS = [ 'exe' , 'dll' , 'bat' , 'sh' , 'cmd' , 'ps1' ]
def allowed_file ( filename ):
return '.' in filename and \
filename.rsplit( '.' , 1 )[ 1 ].lower() not in BLOCKED_EXTENSIONS
Database-Level Constraints
Several constraints are enforced at the database level for data integrity.
Unique Constraints
-- Usernames must be unique
CREATE TABLE users (
username TEXT UNIQUE NOT NULL ,
email TEXT UNIQUE NOT NULL
)
-- Room names must be unique
CREATE TABLE rooms (
name TEXT UNIQUE NOT NULL
)
-- One vote per user per post
CREATE TABLE post_votes (
UNIQUE (post_id, username)
)
-- No duplicate follow relationships
CREATE TABLE following (
UNIQUE (follower, following )
)
Foreign Key Constraints
Foreign keys maintain referential integrity:
-- Room members must reference valid rooms and users
CREATE TABLE room_members (
room_id INTEGER NOT NULL ,
username TEXT NOT NULL ,
FOREIGN KEY (room_id) REFERENCES rooms(id),
FOREIGN KEY (username) REFERENCES users(username)
)
Foreign key constraints prevent orphaned data and maintain consistency across tables.
Rate Limiting Considerations
The current codebase doesn’t implement rate limiting, which is a significant gap in content moderation.
Missing feature : Rate limiting should be implemented to prevent:
Spam posting
Brute force attacks
Resource exhaustion
API abuse
Recommended Rate Limits
Suggested Rate Limit Implementation
# Suggested implementation (not in current codebase)
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
limiter = Limiter(
app,
key_func = get_remote_address,
default_limits = [ "200 per day" , "50 per hour" ]
)
@posts_bp.route ( '/api/create_post' , methods = [ 'POST' ])
@limiter.limit ( "10 per minute" )
def create_post ():
# Post creation logic
pass
@chat_bp.route ( '/api/send_room_message' , methods = [ 'POST' ])
@limiter.limit ( "30 per minute" )
def send_room_message ():
# Message sending logic
pass
Content Moderation Best Practices
Input validation
Validate all user input at the application layer before processing. # Always strip and validate
room_name = data.get( 'room_name' , '' ).strip()
if not room_name:
return jsonify({ 'error' : 'invalid fields' }), 400
Output sanitization
Sanitize content before rendering to prevent XSS attacks. safe_content = safe_markdown(user_content)
Authentication checks
Always validate authentication tokens before processing requests. token = request.headers.get( 'Authorization' )
if not token:
return jsonify({ 'error' : 'unauthorized' }), 401
Authorization checks
Verify user permissions for the requested action. if not user_is_room_member(room_id, username):
return jsonify({ 'error' : 'forbidden' }), 403
Monitoring and Logging
The server includes basic logging for debugging:
# From app/routes/upload.py:35-42
print ( f "Attempting to upload file: { file .filename } to room { room_id } " )
try :
file_url = file_uploader( file )
print ( f "File uploaded successfully: { file_url } " )
except Exception as e:
print ( f "File upload failed: { str (e) } " )
Production recommendation : Replace print statements with a proper logging framework (e.g., Python’s logging module) to track:
Failed authentication attempts
Content creation rates
File upload activities
Error patterns
The codebase uses Flask-CORS for cross-origin requests:
# From app/routes/upload.py:13
@upload_bp.route ( '/api/upload_file' , methods = [ 'POST' ])
@cross_origin ()
def upload_file ():
Recommended Security Headers
Future Moderation Improvements
Rate Limiting Implement per-user and per-endpoint rate limits to prevent abuse
File Type Validation Restrict file uploads to safe file types and scan for malware
Content Length Limits Add character limits to descriptions, usernames, and other text fields
CSS Sanitization Implement server-side CSS validation for custom profile styles
Automated Profanity Filter Add optional profanity filtering for messages and posts
Report System Allow users to report inappropriate content for moderator review
Moderation Dashboard Create admin interface for reviewing reported content
Ban/Mute System Implement user and content moderation actions
API Moderation Reference
Protected Endpoints
All these endpoints require valid authentication:
POST /api/create_room - Room creation (with public room limit)
POST /api/join_room - Room joining (with password check)
POST /api/send_room_message - Message sending (with membership check)
GET /api/get_room_messages - Message retrieval (with membership check)
POST /api/upload_file - File upload (with size and membership checks)
POST /api/create_post - Post creation (with length limit)
POST /api/vote_post - Voting (with self-vote prevention)
POST /api/user/settings - Profile updates (with email uniqueness)
Moderation Status Codes
Code Meaning Typical Cause 400Bad Request Invalid input, length exceeded, duplicate data 401Unauthorized Missing or invalid auth token 403Forbidden Insufficient permissions, self-voting, not a member 404Not Found Resource doesn’t exist 500Server Error Database error, external service failure
Next Steps
Privacy Settings Learn about privacy controls and data protection
Creating Rooms Understand room creation and management