VYPR
Medium severityNVD Advisory· Published Jan 17, 2025· Updated Apr 15, 2026

CVE-2025-23205

CVE-2025-23205

Description

nbgrader is a system for assigning and grading notebooks. Enabling frame-ancestors: 'self' grants any JupyterHub user the ability to extract formgrader content by sending malicious links to users with access to formgrader, at least when using the default JupyterHub configuration of enable_subdomains = False. #1915 disables a protection which would allow user Alice to craft a page embedding formgrader in an IFrame. If Bob visits that page, his credentials will be sent and the formgrader page loaded. Because Alice's page is on the same Origin as the formgrader iframe, Javasript on Alice's page has _full access_ to the contents of the page served by formgrader using Bob's credentials. This issue has been addressed in release 0.9.5 and all users are advised to upgrade. Users unable to upgrade may disable frame-ancestors: self, or enable per-user and per-service subdomains with JupyterHub.enable_subdomains = True (then even if embedding in an IFrame is allowed, the host page does not have access to the contents of the frame).

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
nbgraderPyPI
>= 0.9.4, < 0.9.50.9.5

Patches

1
73e137511ac1

Revert #1915 for security reason (#1947)

https://github.com/jupyter/nbgraderNicolas BrichetJan 17, 2025via ghsa
7 files changed · +85 4
  • demos/demo_multiple_classes/jupyter_server_config.py+6 0 added
    @@ -0,0 +1,6 @@
    +c = get_config()
    +
    +c.ServerApp.tornado_settings = {}
    +c.ServerApp.tornado_settings["headers"] = {
    +    "Content-Security-Policy": "frame-ancestors 'self'"
    +}
    
  • demos/demo_one_class_multiple_graders/jupyter_server_config.py+6 0 added
    @@ -0,0 +1,6 @@
    +c = get_config()
    +
    +c.ServerApp.tornado_settings = {}
    +c.ServerApp.tornado_settings["headers"] = {
    +    "Content-Security-Policy": "frame-ancestors 'self'"
    +}
    
  • demos/demo_one_class_one_grader/jupyter_server_config.py+6 0 added
    @@ -0,0 +1,6 @@
    +c = get_config()
    +
    +c.ServerApp.tornado_settings = {}
    +c.ServerApp.tornado_settings["headers"] = {
    +    "Content-Security-Policy": "frame-ancestors 'self'"
    +}
    
  • demos/utils.sh+1 0 modified
    @@ -58,6 +58,7 @@ setup_jupyterhub () {
     
         # Copy config file.
         cp jupyterhub_config.py "${jupyterhub_root}/jupyterhub_config.py"
    +    cp jupyter_server_config.py /usr/local/etc/jupyter/jupyter_server_config.py
     }
     
     enable_create_assignment () {
    
  • nbgrader/docs/source/configuration/images/jupyterhub_4.1_iframe.png+0 0 added
  • nbgrader/docs/source/configuration/jupyterhub_config.rst+66 0 modified
    @@ -20,6 +20,20 @@ Using nbgrader with JupyterHub
             much required reading if you want to integrate the formgrader with
             JupyterHub.
     
    +.. warning::
    +
    +    For security reasons, ``iframe`` are not allowed with JupyterHub from version 4.1. The
    +    documentation about this security change is at
    +    `mitigating-same-origin-deployments <https://jupyterhub.readthedocs.io/en/stable/explanation/websecurity.html#mitigating-same-origin-deployments>`_.
    +
    +    In the current version of nbgrader, the ``formgrader`` UI is embedded in an ``iframe``, to
    +    be available in a new tab of Jupyterlab or Notebook. Therefore, the ``formgrader`` UI can't
    +    be loaded when using ``jupyterhub>=4.1``, and shows a blank panel instead.
    +
    +    There are several ways to use the ``formgrader`` with ``jupyterhub>=4.1``, see details
    +    at :ref:`jupyterhub-4.1`.
    +
    +
     For instructors running a class with JupyterHub, nbgrader offers several tools
     that optimize and enrich the instructors' and students' experience of sharing
     the same system. By integrating with JupyterHub, nbgrader streamlines the
    @@ -331,3 +345,55 @@ API
         .. automethod:: add_student_to_course
     
         .. automethod:: remove_student_from_course
    +
    +
    +.. _jupyterhub-4.1:
    +
    +Formgrader with ``jupyterhub>=4.1``
    +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    +
    +As explained above in the warning, ``jupyterhub>=4.1`` does not allow iframe for security
    +reasons, which lead to blank panel instead of the ``formgrader`` UI.
    +
    +Below are different ways to use the ``formgrader`` UI with ``jupyterhub>=4.1``.
    +
    +Opening the ``formgrader`` UI in a new browser tab
    +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    +
    +Web browsers are able to open iframes in a new browser tab, which allows using the
    +``formgrader`` without any additional setting on the jupyterhub server.
    +For example with Firefox, right clicking on the iframe shows a context menu to open the
    +contents in a new browser tab.
    +
    +.. image:: images/jupyterhub_4.1_iframe.png
    +
    +Although this solution isn't the most practical, it does allow to use ```formgrader``
    +without having to update the configuration and without adding vulnerabilities to the application.
    +
    +Enabling JupyterHub subdomains
    +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    +
    +Enabling per-user and per-service subdomains with ``JupyterHub.enable_subdomains = True``
    +allows to securely use iframes with JupyterHub.
    +With subdomains enabled, `frame-ancestors 'self'` allows embedding the iframe only on pages
    +served by the user's own server.
    +
    +In this case, the ``"frame-ancestor 'self'"`` can be restored in the application:
    +
    +.. code:: python
    +
    +    c.ServerApp.tornado_settings = {}
    +    c.ServerApp.tornado_settings["headers"] = {
    +        "Content-Security-Policy": "frame-ancestors 'self'"
    +    }
    +
    +in e.g. ``/usr/local/etc/jupyter/jupyter_server_config.py``.
    +
    +Trusting users (less secure)
    +^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    +
    +If you trust users and are aware of the security vulnerability, it is also possible to
    +enable the iframe with the same configuration as above, without subdomains.
    +
    +This is the solution used in the JupyterHub docker
    +`demo <https://github.com/jupyter/nbgrader/tree/main/demos>`_.
    
  • nbgrader/server_extensions/formgrader/base.py+0 4 modified
    @@ -60,10 +60,6 @@ def api(self):
             api.log_level = level
             return api
     
    -    def initialize(self):
    -        super().initialize()
    -        self.set_header("Content-Security-Policy", "frame-ancestors 'self'")
    -
         def render(self, name, **ns):
             template = self.settings['nbgrader_jinja2_env'].get_template(name)
             return template.render(**ns)
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

7

News mentions

0

No linked articles in our index yet.