Skip to content

Posts received with form elements are rendered allow submission

Moderate
dahlia published GHSA-w7gc-g3x7-hq8h Jul 17, 2025

Package

docker ghcr.io/fedify-dev/hollo (Docker)

Affected versions

<= 0.6.4

Patched versions

0.6.5

Description

Summary

When an incoming post has form elements included, the elements are rendered and are submittable. Other platforms normally remove such elements before rendering.

Please note that I am an end user of Hollo with some dev experience. I've done my best to research the affected code, but I wanted this to be clear before you dive into the details.

Details

When a platform, such as Ghost or Wordpress, announces a new note, it can include HTML elements that may or may not be allowed on receiving platforms, like javascript, css, and form elements. It appears most platforms strip out such data before displaying the note to end users.

I noticed the issue when looking at the Fediverse Report, which has recently moved to a Wordpress site hosted at connectedplaces.online. @[email protected]

Here's the post that had the form element - a mailing list submission form.

https://connectedplaces.online/reports/atmosphere-report-122/

I want to be clear I am not well versed in this codebase, and did only a surface scan for terms like "sanitize" or "purify".

A quick scan of the fedify and hollo codebase shows an example implementation using sanitize-html but this does not seem to be utilized by Hollo itself.

Currently I see this formatText function using MarkdownIt. Here, an environment variable determines if HTML should be allowed or not.

Note that I checked my Docker based Hollo environment variables, and I have NOT set this value ALLOW_HTML. It appears based on this code that it SHOULD be set to false by default. Still, these form elements are showing up.

Regardless of what I set this value to, even when I set this value to FALSE, the form elements are still showing up.

Even if HTML is allowed, I would think you would want SOME constraints around the use of javascript and form submissions, at least.

Using MarkdownIt's demo site I see if you check the HTML checkbox, you can definitely still add form elements and submit the form. This validates what I'm seeing in Hollo since this is the sanitizing code there as well.

<form action="https://hollo.social/"><input><button>Click Me</button>

Also alarming is that I can inject javascript here as well:

<script>alert("Test");</script>

And at least in this demo page, it works. I did NOT test this in Hollo, but since it's using MarkdownIt, I assume it's possible?

PoC

Current Hollo Configuration: Docker Compose
Current Version (as of this morning): Version 0.7.0-dev.59
Running through Portainer / NGINX on my Homelab server.
Connecting to Hollo via Phanpy to view posts.

services:
  hollo:
    image: ghcr.io/fedify-dev/hollo:canary
    ports:
      - "3131:3000"
    environment:
      DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}
      SECRET_KEY: ${SECRET_KEY}
      LOG_LEVEL: ${LOG_LEVEL}
      BEHIND_PROXY: ${BEHIND_PROXY}
      DRIVE_DISK: s3
      STORAGE_URL_BASE: "https://hollo-media.box464.social/"
      S3_REGION: us-east-1
      S3_BUCKET: hollo
      S3_ENDPOINT_URL: https://606244c31ccee0df06b79e851024e327.r2.cloudflarestorage.com
      S3_FORCE_PATH_STYLE: "true"
      AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID}
      AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY}
    depends_on:
      postgres:
        condition: service_healthy
    networks:
      - hollo_net
    restart: unless-stopped

  postgres:
    image: postgres:17
    environment:
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: ${POSTGRES_DB}
      PGDATA: /var/lib/postgresql/data/pgdata
    volumes:
      - /home/box464/docker-volumes/hollo/data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
      interval: 10s
      timeout: 5s
      retries: 10
      start_period: 30s
    networks:
      - hollo_net
    restart: unless-stopped

networks:
  hollo_net:
    driver: bridge

From Phanpy, I searched for the account: @[email protected]

And then looked for the post from June 26th, ATmosphere Report - #122.

https://connectedplaces.online/reports/atmosphere-report-122/

A form input appears here with a button to submit your email address to join their newsletter. I submitted a fake email address and it does complete the submission.

Here's a video showing the issue.

Hollo Demo (private link)

Impact

This is an HTML injection vulnerability that affects all Hollo users. Without proper HTML sanitization, malicious actors could attempt:

Forms to harvest personal information through phishing
External data collection without user consent or knowledge (as demonstrated by the working newsletter signup), especially if javascript is allowed.
CSRF attacks via embedded forms that trick users into performing unintended actions

Please reach out to me directly if needed. I am more likely to respond on Mastodon at @[email protected]. I am also in the Fedify Discord with the same alias.

Severity

Moderate

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
None
User interaction
Required
Scope
Changed
Confidentiality
Low
Integrity
Low
Availability
None

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N

CVE ID

CVE-2025-53941

Weaknesses

Improper Input Validation

The product receives input or data, but it does not validate or incorrectly validates that the input has the properties that are required to process the data safely and correctly. Learn more on MITRE.

Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')

The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users. Learn more on MITRE.

Improper Neutralization of Alternate XSS Syntax

The product does not neutralize or incorrectly neutralizes user-controlled input for alternate script syntax. Learn more on MITRE.

Improper Encoding or Escaping of Output

The product prepares a structured message for communication with another component, but encoding or escaping of the data is either missing or done incorrectly. As a result, the intended structure of the message is not preserved. Learn more on MITRE.

Incomplete List of Disallowed Inputs

The product implements a protection mechanism that relies on a list of inputs (or properties of inputs) that are not allowed by policy or otherwise require other action to neutralize before additional processing takes place, but the list is incomplete, leading to resultant weaknesses. Learn more on MITRE.

Exposure of Sensitive Information to an Unauthorized Actor

The product exposes sensitive information to an actor that is not explicitly authorized to have access to that information. Learn more on MITRE.

Cross-Site Request Forgery (CSRF)

The web application does not, or can not, sufficiently verify whether a well-formed, valid, consistent request was intentionally provided by the user who submitted the request. Learn more on MITRE.

Credits