This document summarizes the investigation into embedding custom JavaScript applications within a Moodle dashboard, showing the initial analysis and the final conclusion based on live testing.
-
No Content-Security-Policy (CSP): The HTTP headers for the Moodle page were missing a
Content-Security-Policy(CSP) header. A CSP header is the primary browser-level mechanism to control which external resources (like scripts) can be loaded. Its absence suggested that loading external scripts would be possible. -
HTML Sanitization Appears Minimal: An initial test of adding a simple HTML block with
<div>and<h3>tags was successful, indicating that Moodle's own sanitization was not overly aggressive. -
x-frame-optionsis Not a Blocker: Thex-frame-options: sameoriginheader was present, but this only prevents the page from being iframed by other domains; it does not block the page from loading external content itself.
- A test to save a custom HTML block containing a
<script>tag resulted in a 403 Forbidden error from Cloudflare. - A subsequent test to save a custom HTML block containing an
<iframe>tag was successful.
The initial analysis was correct at the browser and Moodle level, but the definitive factor is the Cloudflare Web Application Firewall (WAF). The WAF intercepts the request to save the HTML, identifies the <script> tag as a potential Cross-Site Scripting (XSS) attack, and blocks the request before it reaches Moodle.
However, the WAF permits the use of <iframe> tags.
The definitive way to embed your custom application is to:
- Build your application with a minimal JS framework as a standalone web page.
- Host this application on any web server or static hosting service (see below).
- Embed the hosted application into the Moodle custom HTML block using an
<iframe>tag.
Example:
<iframe src="https://your-app-host.com/index.html" width="100%" height="500px" style="border:none;"></iframe>This approach bypasses the WAF's script-blocking rule while still allowing you to render your custom application inside the Moodle dashboard.
GitHub Pages is an excellent choice for hosting the embedded application for the following reasons:
- No
X-Frame-OptionsHeader: GitHub Pages servers do not send theX-Frame-Options: denyheader, meaning they are explicitly designed to be embedded in iframes across different domains. - No Moodle-side CSP: The target Moodle page does not have a
Content-Security-Policy(CSP) with aframe-srcdirective, so the browser will not be instructed to block the iframe.
To provide dynamic content, the iframed application can make API calls to its own backend, hosted on a separate domain. This interaction is controlled by CORS (Cross-Origin Resource Sharing).
- The application (e.g.,
https://your-name.github.io) makes afetchrequest to your API (e.g.,https://api.your-domain.com). - To allow this, your backend API server must be configured to send a specific HTTP header in its responses, trusting the origin of your application.
Required API Response Header:
Access-Control-Allow-Origin: https://your-name.github.io
This configuration on your API server will instruct the browser to permit the cross-domain request, allowing your embedded app to fetch and display dynamic data. Moodle and Cloudflare are not involved in this part of the process.