Skip to main content

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)
1

Markdown conversion

Raw text is converted to HTML using the Python markdown library.
2

HTML sanitization

The resulting HTML is cleaned to remove any dangerous tags or attributes.
3

Safe output

Only whitelisted tags and attributes are allowed in the final output.

Allowed HTML Tags

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'
]
TagPurposeSecurity Risk
aLinksControlled via href attribute
abbr, acronymAbbreviationsSafe
b, strong, em, iText formattingSafe
blockquoteQuotationsSafe
code, preCode blocksSafe
ul, ol, liListsSafe
p, brParagraphs and breaksSafe
h1-h4HeadersSafe
imgImagesControlled 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
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

FieldTypeConstraintsModeration
UsernamestringUnique, requiredSet at registration
EmailstringUnique, required, privateFormat validation recommended
DescriptiontextOptionalNo length limit (should be added)
Avatar URLstringURL formatShould validate URL format
Custom CSStextOptionalSecurity risk - needs sanitization
Background ImagestringURL formatShould 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.
# 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
# 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

1

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
2

Output sanitization

Sanitize content before rendering to prevent XSS attacks.
safe_content = safe_markdown(user_content)
3

Authentication checks

Always validate authentication tokens before processing requests.
token = request.headers.get('Authorization')
if not token:
    return jsonify({'error': 'unauthorized'}), 401
4

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

Security Headers

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():

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

CodeMeaningTypical Cause
400Bad RequestInvalid input, length exceeded, duplicate data
401UnauthorizedMissing or invalid auth token
403ForbiddenInsufficient permissions, self-voting, not a member
404Not FoundResource doesn’t exist
500Server ErrorDatabase error, external service failure

Next Steps

Privacy Settings

Learn about privacy controls and data protection

Creating Rooms

Understand room creation and management

Build docs developers (and LLMs) love