Checkmate icon indicating copy to clipboard operation
Checkmate copied to clipboard

feat: Status Page Custom CSS/JS/HTML overrides (Fixes #2863)

Open Armaansaxena opened this issue 1 month ago • 4 comments

Describe your changes

I have implemented the ability for admins to customize their Status Pages with custom CSS, JavaScript, and HTML overrides for the Header and Footer. This allows teams to brand their status pages without forking the source code.

Backend Changes:

  • Updated StatusPage Mongoose schema to include customJavaScript, headerHTML, and footerHTML (added to existing customCSS).
  • Updated Joi validation (server/validation/joi.js) to allow these string fields during creation and updates.

Frontend Changes:

  • Updated Settings.jsx to include input fields for these new options, ensuring correct name attributes for form saving.
  • Added translation keys to en.json to ensure no hardcoded strings are used in the UI.
  • Updated Settings.jsx to inject the custom CSS/JS via useEffect and conditionally render the custom HTML Header/Footer if they exist in the database.

Write your issue number after "Fixes "

Fixes #2863

Please ensure all items are checked off before requesting a review. "Checked off" means you need to add an "x" character between brackets so they turn into checkmarks.

  • [x] (Do not skip this or your PR will be closed) I deployed the application locally.
  • [x] (Do not skip this or your PR will be closed) I have performed a self-review and testing of my code.
  • [x] I have included the issue # in the PR.
  • [x] I have added i18n support to visible strings (instead of <div>Add</div>, use):
const { t } = useTranslation();
<div>{t('add')}</div>

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

* **New Features**
  * Add fields to enter custom CSS, header HTML, footer HTML, and custom JavaScript for Status Pages; these are applied to the public status page with proper lifecycle handling.
* **Documentation**
  * New UI labels, descriptions, placeholders and a JavaScript warning helper for customization fields.
* **Validation**
  * Server-side validation enforces optional fields with length limits for CSS, header/footer HTML, and custom JavaScript.

<sub>✏️ Tip: You can customize this high-level summary in your review settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Armaansaxena avatar Nov 29 '25 08:11 Armaansaxena

[!NOTE]

.coderabbit.yml has unrecognized properties

CodeRabbit is using all valid settings from your configuration. Unrecognized properties (listed below) have been ignored and may indicate typos or deprecated fields that can be removed.

⚠️ Parsing warnings (1)
Validation error: Unrecognized key(s) in object: 'release_notes'
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Walkthrough

Adds customizable fields (Custom CSS, Header HTML, Footer HTML, Custom JavaScript) to the admin UI, schema, validation, and public API; injects and sanitizes CSS/HTML/JS on the public status page client; adds i18n keys and a dompurify dependency.

Changes

Cohort / File(s) Change Summary
Admin UI — Settings form
client/src/Pages/v1/StatusPage/Create/Components/Tabs/Settings.jsx
Added multiline TextFields bound to form.customCSS, form.headerHTML, form.footerHTML, form.customJavaScript; added riskAccepted checkbox gating JS field and an effect that checks it when JS exists; wired to existing handleFormChange.
Status page rendering (client)
client/src/Pages/v1/StatusPage/Status/index.jsx
Added DOMPurify and useEffect to sanitize and render headerHTML/footerHTML; injects <style> with customCSS into document.head; injects/executed customJavaScript inside an IIFE with try/catch into document.body; cleans up injected nodes on unmount.
Localization
client/src/locales/en.json
Added i18n keys for customCSS, customHeaderHTML, customFooterHTML, customJavaScript plus descriptions, placeholders, and a JS warning string.
Client dependencies / build
client/package.json
Added dompurify (^3.3.0) and bumped vite to ^6.4.1.
Database schema (server)
server/src/db/v1/models/StatusPage.js
Added optional string fields customJavaScript (maxLength 5000), headerHTML (maxLength 10000), and footerHTML (maxLength 10000) with default "".
Request validation (server)
server/src/validation/joi.js
Extended createStatusPageBodyValidation with optional customCSS (max 10000), customJavaScript (max 5000), headerHTML (max 5000), and footerHTML (max 5000).
StatusPage module — public API shape
server/src/db/v1/modules/statusPageModule.js
Included customCSS, customJavaScript, headerHTML, and footerHTML in early-return and aggregation projection so public GET returns these fields.

Sequence Diagram(s)

sequenceDiagram
    actor Admin
    participant SettingsUI as Admin Settings (client)
    participant API as Backend API
    participant DB as Database
    participant Browser as Public Status Page (browser)

    Admin->>SettingsUI: Edit CSS / HeaderHTML / FooterHTML / JS
    SettingsUI->>SettingsUI: Update form state
    Admin->>SettingsUI: Save
    SettingsUI->>API: POST status page payload (includes new fields)
    API->>API: Validate with Joi (new fields)
    API->>DB: Persist StatusPage document
    DB-->>API: OK
    API-->>SettingsUI: Success

    Browser->>API: GET statusPage config
    API->>DB: Query status page (projection includes new fields)
    DB-->>API: Config
    API-->>Browser: Config
    Browser->>Browser: Sanitize headerHTML/footerHTML (DOMPurify)
    Browser->>Browser: Inject <style> with customCSS into document.head
    Browser->>Browser: Inject/evaluate customJavaScript in IIFE (try/catch) into document.body
    Browser->>Browser: Render sanitized header/footer or fallback components
    Browser->>Browser: Cleanup injected nodes on unmount

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

  • Inspect client-side injection and sanitization in client/src/Pages/v1/StatusPage/Status/index.jsx (DOMPurify usage, sanitization configuration, cleanup).
  • Verify Joi limits in server/src/validation/joi.js align with schema maxLength in server/src/db/v1/models/StatusPage.js.
  • Confirm server/src/db/v1/modules/statusPageModule.js includes new fields consistently in early-return and aggregation projection.
  • Ensure client/src/Pages/v1/StatusPage/Create/Components/Tabs/Settings.jsx field names match validation and are persisted in payload.
  • Check client/package.json dependency changes for build compatibility.

Poem

🐇
I nibble styles and tuck a script away,
A header, footer—cleaned for bright display,
I hop through markup, sanitize with care,
Nest CSS in head, JS in a guarded lair,
Happy hops—your status page now wears flair! 🥕✨

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Linked Issues check ❓ Inconclusive While core features (custom CSS/HTML/JS fields, DOMPurify sanitization, UI inputs, i18n) are implemented, several issue requirements lack clear implementation: live preview, export/import JSON, dedicated syntax-highlighting editors, CSP enforcement, audit logging, and explicit risk acknowledgement in UI for JavaScript. Verify that DOMPurify sanitization, risk acceptance UI gate for JavaScript, and critical security measures (CSP, audit logging) are fully implemented; confirm whether preview/export/import are deferred to follow-up work.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title concisely summarizes the main change: adding custom CSS/JS/HTML overrides to status pages, referencing the associated issue #2863.
Description check ✅ Passed The description covers all major changes, lists backend and frontend modifications, includes the issue reference, and shows most checklist items completed; minor incomplete items do not materially impact overall adequacy.
Out of Scope Changes check ✅ Passed All changes directly support the linked issue requirements: schema extensions, validation rules, frontend UI inputs, i18n localization, and sanitization. No unrelated modifications detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • [ ] 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • [ ] Create PR with unit tests
  • [ ] Post copyable unit tests in a comment

📜 Recent review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f99222cf61c0b0ebbc9b99b4d93ec8aa7cb5cbac and 63b5912602ca9bd0c8fea9c76aae51458e4c2935.

📒 Files selected for processing (1)
  • client/src/Pages/v1/StatusPage/Create/Components/Tabs/Settings.jsx (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • client/src/Pages/v1/StatusPage/Create/Components/Tabs/Settings.jsx

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

coderabbitai[bot] avatar Nov 29 '25 08:11 coderabbitai[bot]

The headerHTML and footerHTML fields introduce raw HTML injection into the page. As CodeRabbit is warning, this is essentially creating an critical XSS risk. I understand that the feature is admin-only but rendering untrusted HTML and directly injecting JS is a bad practice overall.

SajanGhuman avatar Nov 30 '25 16:11 SajanGhuman

@SajanGhuman @ajhollid Thank you for the critical review. I fully agree with the security concerns raised regarding the XSS risks and arbitrary code execution.

My plan to fix this:

HTML Sanitization: I will implement DOMPurify to sanitize both headerHTML and footerHTML before rendering, ensuring no malicious scripts can be injected via these fields.

Backend Limits: I will update the Joi validation to enforce strict character limits (as suggested by CodeRabbit) to prevent database performance issues.

Custom JS: I understand the risk here. Since this was a requested feature in issue #2863 (allowing admins to override behavior), I will add a strict "Risk Acceptance" warning in the UI that the admin must check before saving.

If you believe customJS is too dangerous even with admin warnings, please let me know, and I can remove that specific field and limit the scope to just CSS and Sanitized HTML.

I will push these fixes shortly.

Armaansaxena avatar Nov 30 '25 18:11 Armaansaxena

Hi @Armaansaxena,

Perhaps the solution is to gate this feature behind some sort of approval process whereby the admin acknowledges the risks of allowing arbitrary code to be executed.

@SajanGhuman @gorkemcetin what do you think?

ajhollid avatar Nov 30 '25 18:11 ajhollid

Hi @Armaansaxena,

Perhaps the solution is to gate this feature behind some sort of approval process whereby the admin acknowledges the risks of allowing arbitrary code to be executed.

@SajanGhuman @gorkemcetin what do you think?

I agree. There should be some kind of warning explaining the risks that the admin is aware of and acknowledges to.

SajanGhuman avatar Dec 01 '25 19:12 SajanGhuman

Hi @Armaansaxena, Perhaps the solution is to gate this feature behind some sort of approval process whereby the admin acknowledges the risks of allowing arbitrary code to be executed. @SajanGhuman @gorkemcetin what do you think?

I agree. There should be some kind of warning explaining the risks that the admin is aware of and acknowledges to.

Just a warning should be fine.

gorkem-bwl avatar Dec 02 '25 21:12 gorkem-bwl

@gorkem-bwl @SajanGhuman @ajhollid

Update: All requested security controls and fixes are implemented.

Based on the feedback, I have finalized the security model for this feature:

  • JS Risk Gatekeeper (Implemented): The "Custom JavaScript" input is now disabled by default. The Admin must strictly check a "Risk Acceptance" checkbox to unlock the field.

  • CSS Sanitization (Hardened): Per CodeRabbit's feedback, I updated the CSS injection to strictly block dangerous patterns (e.g., url(), @import, behavior, javascript:).

  • HTML Sanitization: Full DOMPurify implementation for Header/Footer is in place.

  • Backend Validation: Added strict maxLength constraints to the schema.

  • Fixes: Resolved the i18n fallback syntax and variable naming conflicts.

The feature is now secure, gated, and validates all inputs. Ready for merge!

Armaansaxena avatar Dec 03 '25 06:12 Armaansaxena