Fix security issues.
This commit is contained in:
@@ -8,8 +8,9 @@ from sqlalchemy import and_, or_
|
||||
|
||||
# Local application imports
|
||||
from models import (Note, NoteFolder, NoteLink, NoteVisibility, Project,
|
||||
Task, db)
|
||||
Task, UserPreferences, db)
|
||||
from routes.auth import company_required, login_required
|
||||
from security_utils import sanitize_folder_path, validate_folder_access
|
||||
|
||||
# Create blueprint
|
||||
notes_bp = Blueprint('notes', __name__, url_prefix='/notes')
|
||||
@@ -30,6 +31,16 @@ def notes_list():
|
||||
visibility_filter = request.args.get('visibility', '')
|
||||
search_query = request.args.get('search', '')
|
||||
|
||||
# Sanitize folder filter if provided
|
||||
if folder_filter:
|
||||
try:
|
||||
folder_filter = sanitize_folder_path(folder_filter)
|
||||
# Validate folder exists
|
||||
if not validate_folder_access(folder_filter, g.user.company_id, db.session):
|
||||
folder_filter = '' # Reset to root if invalid
|
||||
except ValueError:
|
||||
folder_filter = '' # Reset to root if invalid
|
||||
|
||||
# Base query - only non-archived notes for the user's company
|
||||
query = Note.query.filter_by(
|
||||
company_id=g.user.company_id,
|
||||
@@ -197,6 +208,41 @@ def create_note():
|
||||
task_id = request.form.get('task_id')
|
||||
is_pinned = request.form.get('is_pinned') == '1'
|
||||
|
||||
# Sanitize and validate folder if provided
|
||||
if folder:
|
||||
try:
|
||||
folder = sanitize_folder_path(folder)
|
||||
# Ensure folder exists or create it
|
||||
if not validate_folder_access(folder, g.user.company_id, db.session):
|
||||
# Create folder hierarchy if it doesn't exist
|
||||
folder_parts = folder.split('/')
|
||||
current_path = ''
|
||||
for i, part in enumerate(folder_parts):
|
||||
if i == 0:
|
||||
current_path = part
|
||||
else:
|
||||
current_path = current_path + '/' + part
|
||||
|
||||
existing = NoteFolder.query.filter_by(
|
||||
company_id=g.user.company_id,
|
||||
path=current_path
|
||||
).first()
|
||||
|
||||
if not existing:
|
||||
parent_path = '/'.join(folder_parts[:i]) if i > 0 else None
|
||||
new_folder = NoteFolder(
|
||||
name=part,
|
||||
path=current_path,
|
||||
parent_path=parent_path,
|
||||
company_id=g.user.company_id,
|
||||
created_by_id=g.user.id
|
||||
)
|
||||
db.session.add(new_folder)
|
||||
db.session.flush() # Ensure folder is created before continuing
|
||||
except ValueError as e:
|
||||
flash(f'Invalid folder path: {str(e)}', 'error')
|
||||
return redirect(url_for('notes.create_note'))
|
||||
|
||||
# Validate
|
||||
if not title:
|
||||
flash('Title is required', 'error')
|
||||
@@ -366,6 +412,18 @@ def edit_note(slug):
|
||||
task_id = request.form.get('task_id')
|
||||
is_pinned = request.form.get('is_pinned') == '1'
|
||||
|
||||
# Sanitize and validate folder if provided
|
||||
if folder:
|
||||
try:
|
||||
folder = sanitize_folder_path(folder)
|
||||
# Validate folder exists
|
||||
if not validate_folder_access(folder, g.user.company_id, db.session):
|
||||
flash('Invalid folder selected', 'error')
|
||||
return redirect(url_for('notes.edit_note', slug=slug))
|
||||
except ValueError as e:
|
||||
flash(f'Invalid folder path: {str(e)}', 'error')
|
||||
return redirect(url_for('notes.edit_note', slug=slug))
|
||||
|
||||
# Validate
|
||||
if not title:
|
||||
flash('Title is required', 'error')
|
||||
@@ -495,4 +553,30 @@ def notes_folders():
|
||||
folders=folders,
|
||||
folder_tree=folder_tree,
|
||||
folder_counts=folder_counts,
|
||||
title='Manage Folders')
|
||||
title='Manage Folders')
|
||||
|
||||
|
||||
@notes_bp.route('/preferences', methods=['POST'])
|
||||
@login_required
|
||||
@company_required
|
||||
def update_note_preferences():
|
||||
"""Update note-related user preferences"""
|
||||
note_preview_font = request.form.get('note_preview_font', 'system')
|
||||
|
||||
# Get or create user preferences
|
||||
preferences = UserPreferences.query.filter_by(user_id=g.user.id).first()
|
||||
if not preferences:
|
||||
preferences = UserPreferences(user_id=g.user.id)
|
||||
db.session.add(preferences)
|
||||
|
||||
# Update preferences
|
||||
preferences.note_preview_font = note_preview_font
|
||||
|
||||
db.session.commit()
|
||||
|
||||
# Return JSON response for AJAX calls
|
||||
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
|
||||
return jsonify({'success': True, 'font': note_preview_font})
|
||||
|
||||
# Otherwise redirect back
|
||||
return redirect(request.referrer or url_for('notes.notes_list'))
|
||||
Reference in New Issue
Block a user