Skip to content

Instantly share code, notes, and snippets.

@tzkmx
Last active September 3, 2025 00:38
Show Gist options
  • Save tzkmx/b9f07c147206563dc54a106357c52da7 to your computer and use it in GitHub Desktop.
Save tzkmx/b9f07c147206563dc54a106357c52da7 to your computer and use it in GitHub Desktop.
Moodle mini app embedded

Analysis of Moodle Dashboard Embedding Capabilities

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.

Initial Analysis (Based on HTTP Headers and HTML)

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

  2. 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.

  3. x-frame-options is Not a Blocker: The x-frame-options: sameorigin header 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.

Live Test Results

  1. A test to save a custom HTML block containing a <script> tag resulted in a 403 Forbidden error from Cloudflare.
  2. A subsequent test to save a custom HTML block containing an <iframe> tag was successful.

Final Conclusion

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.

Recommended Solution

The definitive way to embed your custom application is to:

  1. Build your application with a minimal JS framework as a standalone web page.
  2. Host this application on any web server or static hosting service (see below).
  3. 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.

Hosting Recommendation: GitHub Pages

GitHub Pages is an excellent choice for hosting the embedded application for the following reasons:

  1. No X-Frame-Options Header: GitHub Pages servers do not send the X-Frame-Options: deny header, meaning they are explicitly designed to be embedded in iframes across different domains.
  2. No Moodle-side CSP: The target Moodle page does not have a Content-Security-Policy (CSP) with a frame-src directive, so the browser will not be instructed to block the iframe.

Architecture for Dynamic Content

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 a fetch request 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment