Skip to content

Security: Systemic Authorization Bypass in Whitelisted API Functions #406

@lighthousekeeper1212

Description

@lighthousekeeper1212

Summary

Nearly all @frappe.whitelist() functions in the Education module are callable by any authenticated user (including students) and perform zero permission checks before acting on arbitrary resource IDs. This allows any authenticated user to manipulate fees, enroll students, mark attendance, modify assessment results, and access data for any student.

The permission_query_conditions and has_permission hooks in hooks.py (lines 167-173) are commented out, and there are zero frappe.has_permission() or frappe.only_for() calls in the entire codebase.

Findings (8 vulnerabilities)

1. CRITICAL: Fee Payment Manipulation (collect_fees)

File: education/education/api.py:246-252

Any authenticated user can mark any student's fees as paid:

@frappe.whitelist()
def collect_fees(fees, amt):
    paid_amount = flt(amt) + flt(frappe.db.get_value("Fees", fees, "paid_amount"))
    total_amount = flt(frappe.db.get_value("Fees", fees, "total_amount"))
    frappe.db.set_value("Fees", fees, "paid_amount", paid_amount)
    frappe.db.set_value("Fees", fees, "outstanding_amount", (total_amount - paid_amount))

Uses frappe.db.set_value() which bypasses ORM permissions entirely. No permission check.

2. HIGH: Unauthorized Student Enrollment (enroll_student)

File: education/education/api.py:27-69

Any user can convert Student Applicants into Students with ignore_permissions=True.

3. HIGH: Cross-Student Attendance Marking (mark_attendance)

File: education/education/api.py:90-129

Any user can mark attendance (present/absent) for any student. Accepts arbitrary student IDs in JSON arrays.

4. HIGH: Cross-Student Leave Application (apply_leave)

File: education/education/api.py:606-614

Any user can submit leave for any student. leave_data.get("student") is fully client-controlled.

5. HIGH: Assessment Result Manipulation (mark_assessment_result)

File: education/education/api.py:385-419

Any user can modify assessment scores for any student. scores["student"] and scores["total_score"] are client-controlled.

6. HIGH: Bulk Assessment Submission (submit_assessment_results)

File: education/education/api.py:422-431

Any user can permanently finalize (submit, docstatus=1) assessment results for entire student groups.

7. MEDIUM: Cross-Student Data Disclosure (multiple functions)

File: education/education/api.py

13+ functions use frappe.get_all() (which bypasses permissions) to return data for any student: get_student_guardians, get_student_group_students, get_assessment_students, get_fee_components, get_fee_schedule, get_current_enrollment, get_student_programs, get_student_invoices, get_student_attendance, etc.

Compare with get_student_info() (line 524) which correctly scopes to frappe.session.user.

8. MEDIUM: Unauthorized Email Group Creation (update_email_group)

File: education/education/api.py:454-469

Any user can create Email Groups and populate them with guardian email addresses from any student group.

Root Cause

The developers assumed DocType-level role permissions protect @frappe.whitelist() endpoints - they don't. In Frappe:

  • @frappe.whitelist() makes functions callable by any logged-in user via /api/method/
  • frappe.get_all() bypasses DocType permissions (unlike frappe.get_list())
  • frappe.db.set_value() bypasses all ORM permissions

This matches the pattern found in Frappe HR (Issue #4191), Frappe LMS (Issue #2147), and Frappe CRM (Issue #1787).

Remediation

  1. Uncomment the permission hooks in hooks.py (lines 167-173)
  2. Add frappe.has_permission() checks to all whitelisted functions
  3. Replace frappe.get_all() with frappe.get_list() for student-facing queries
  4. Add frappe.only_for() role restrictions to administrative functions
  5. Replace frappe.db.set_value() with document-level updates that go through permission checks

Found via automated security research by Lighthouse

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions