← Back to Feed CACHED · 2026-05-17 09:42:19 · cache_key CVE-2025-29912
CVE-2025-21609 · CWE-459 · Disclosed 2025-01-03

SiYuan is self-hosted

ASSESSED — NOISGATE V0.5
Vendor
Reassessed
Verdict:
01 · The Real Story

This is a paper shredder wired to a web form, but only in one specific model on one shelf

CVE-2025-21609 is an arbitrary file deletion bug in SiYuan Note 3.1.18. A crafted POST to /api/history/getDocHistoryContent can pass a chosen historyPath; the vulnerable code path would try to parse the target as document history and, on parse failure, call os.RemoveAll on that path. The vendor fix in commit d9887ae adds a workspace-bound path check and removes the delete-on-parse-error behavior, with the fix shipped in 3.1.19.

The vendor's 9.1 CRITICAL score is technically understandable for a trivially reachable destructive action, but it overstates enterprise urgency in the real world. This is a single-version, self-hosted, relatively niche product; official API docs frame the service on 127.0.0.1:6806, while server-style Docker deployments are what turn it into a remotely reachable problem. So the real story is high-risk when exposed, not universally critical by default.

"Internet-exposed SiYuan 3.1.18 is easy to break, but the blast radius is narrowed by niche deployment and a one-version hit."
02 · The Attack Path

4 steps from start to impact.

STEP 01

Reach a live SiYuan kernel service

The attacker needs HTTP reachability to the SiYuan backend on port 6806 or through a reverse proxy terminating to it. Official API docs describe the endpoint as http://127.0.0.1:6806, but official Docker guidance also shows -p 6806:6806, which is how operators convert a local app into an exposed network service.
Conditions required:
  • A SiYuan instance is running
  • The vulnerable instance is exactly 3.1.18
  • The attacker can reach port 6806 directly or via reverse proxy
Where this breaks in practice:
  • Many installs are desktop/local-only rather than Internet-facing
  • Reverse proxies, VPN-only access, or host firewalls often cut off direct reachability
  • The vulnerable population is narrowed to one release
Detection/coverage: Generic exposure scanners can find 6806 or a proxied SiYuan site, but most vuln scanners will rely on version fingerprinting rather than safe proof-based checks.
STEP 02

Send a crafted history request

The GitHub advisory provides a minimal curl reproduction against /api/history/getDocHistoryContent with a chosen absolute historyPath. The vulnerable path accepts attacker-controlled input into GetDocHistoryContent, making exploitation low complexity once the service is reachable.
Conditions required:
  • Ability to issue a POST request with JSON body
  • The endpoint is not blocked by an upstream auth or network control
Where this breaks in practice:
  • Some deployments add access controls in front of the app
  • Official API docs describe token auth, so implementations may vary; defenders should not assume the endpoint is universally unauthenticated
Detection/coverage: Web logs should show POSTs to /api/history/getDocHistoryContent; look for unusual absolute paths or requests from non-user clients.
STEP 03

Trigger delete-on-parse-error behavior

Before the fix, the code attempted to parse the file as document history and, if parsing failed, logged the failure and executed os.RemoveAll(historyPath). The patch removes that deletion behavior and first rejects paths outside the workspace, which is the key exploit break.
Conditions required:
  • The targeted file exists
  • The path resolves on the server filesystem
  • The content does not parse as expected history JSON
Where this breaks in practice:
  • Container filesystem layout can limit what is meaningfully reachable
  • Permissions of the SiYuan process still gate which files can actually be deleted
Detection/coverage: EDR may only see the SiYuan process deleting files, which looks like valid application behavior unless tuned; file-integrity tooling is more useful than signature-only web detection here.
STEP 04

Cause data loss or service damage

Best-case for the attacker is deletion of workspace data, config, or other writable files mounted into the container or available to the process account. That yields integrity and availability damage, but there is no demonstrated code execution or direct confidentiality gain from this CVE alone.
Conditions required:
  • High-value writable files are in scope of the process
  • The attacker knows or can guess useful filesystem paths
Where this breaks in practice:
  • Modern containerization often constrains host-file blast radius
  • The bug is destructive, not directly an initial-access or credential theft primitive
Detection/coverage: Unexpected file deletions by the SiYuan process, sudden workspace corruption, or 5xx errors after requests to the history endpoint are the main signs.
03 · Intelligence Metadata

The supporting signals.

In-the-wild statusNo public active exploitation evidence found in the sources reviewed, and the CVE is not listed in CISA KEV.
Proof of conceptThe GitHub security advisory includes a direct curl reproduction against /api/history/getDocHistoryContent; I did not find a separate weaponized repo in primary sources.
EPSS0.00369 (~0.37%) from the user-provided intel, which is low and matches the absence of exploitation reporting.
KEV statusNot KEV-listed as of the reviewed CISA catalog source.
CVSS interpretationCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:H scores like a clean unauthenticated destructive web bug, but that assumes reachable exposure; in practice, deployment posture does a lot of the scoring work.
Affected versionsExactly 3.1.18 per GitHub/NVD/OSV metadata, not a broad major-version family.
Fixed versions3.1.19 upstream. Downstream distro metadata also shows packaged vuln-db updates in SUSE/openSUSE ecosystems, though that's mainly relevant to advisory feeds rather than SiYuan runtime patching.
Exposure realityOfficial API docs describe the service at http://127.0.0.1:6806, but official Docker docs show -p 6806:6806 and warn to set --accessAuthCode; that means exposure is usually operator-created, not inherently Internet-wide.
DisclosurePublished 2025-01-03; NVD shows later enrichment/modification on 2025-05-14.
Reporter / advisory ownerPublished in GitHub Security Advisory GHSA-8fx8-pffw-w498 by GitHub user 88250.
04 · The Call

noisgate verdict.

Final Verdict
DOWNGRADED to HIGH (7.4/10)

The decisive downgrade factor is reachable population: this is a one-version bug in a niche self-hosted product whose official API documentation points to 127.0.0.1:6806, so many deployments are not broadly exposed. It still lands in HIGH because any Internet-reachable 3.1.18 instance can be hit with a trivial request that causes real integrity and availability damage.

HIGH Technical root cause and fixed version
MEDIUM Real-world exposure prevalence across enterprise deployments
HIGH Lack of current KEV / public active exploitation evidence

Why this verdict

  • Baseline down from vendor CRITICAL: the vendor/NVD score treats this like a generic exposed web app, but SiYuan is a niche self-hosted PKM platform rather than a broadly deployed enterprise edge product.
  • Exact-version narrowing: the affected range is only 3.1.18, which sharply reduces fleet-wide exposure versus a multi-version defect.
  • Exposure is usually operator-created: official docs place the API on 127.0.0.1:6806, while official Docker docs separately show how to publish 6806; that means many instances are local-only or intentionally gated.
  • Impact is destructive, not executable: the bug deletes files and can break service or data, but there is no demonstrated RCE, privilege escalation, or direct data exfiltration primitive in this CVE alone.
  • Threat intel stays cold: no KEV entry and a low EPSS value reduce urgency versus bugs already under live attacker pressure.

Why not higher?

I am not keeping this at CRITICAL because the CVSS headline ignores the biggest real-world friction: not every SiYuan instance is an Internet-facing server. It also hits one exact version and delivers deletion, not code execution. Those are meaningful downward pressures for a defender prioritizing thousands of assets.

Why not lower?

I am not dropping this to MEDIUM because, once a vulnerable instance is reachable, exploitation is simple and low-noise. A single crafted request can delete arbitrary writable files, and that is enough to cause material data loss or operational disruption on an exposed host.

05 · Compensating Control

What to do — in priority order.

  1. Remove public reachability to 6806 — If any SiYuan 3.1.18 instance is Internet-reachable, put it behind VPN, allowlists, or a reverse proxy that is not publicly open. For a HIGH verdict, deploy this containment within 30 days, sooner for any system already exposed externally.
  2. Enforce front-door authentication — Set and verify --accessAuthCode or the equivalent environment variable, and require upstream auth if you reverse-proxy SiYuan. This does not prove the vulnerable endpoint is fully protected in every deployment, but it materially reduces opportunistic abuse; deploy within 30 days.
  3. Constrain filesystem blast radius — Run the service with the least-privileged account possible and keep host mounts minimal so os.RemoveAll can only damage the workspace you intended. That limits worst-case file deletion impact; harden within 30 days.
  4. Add detection on the history endpoint — Alert on POSTs to /api/history/getDocHistoryContent, especially from automation, unusual source IPs, or with suspicious absolute paths. Logging and detections should be in place within 30 days so you can spot abuse before the patch rollout finishes.
  5. Protect restore points — Because this is a deletion bug, offline or immutable backups are your real safety net if somebody hits it before you patch. Validate that workspace snapshots and mounted-volume backups are restorable within 30 days.
What doesn't work
  • Relying on EDR alone: the deletion is performed by the legitimate SiYuan process, so basic behavioral rules may not block it.
  • A generic WAF signature without topology changes: if the service is internal, proxied oddly, or the request body is simple JSON, you may not get reliable prevention.
  • Assuming token auth documentation guarantees safety: the advisory's own PoC is just a bare POST, so do not treat docs alone as a compensating control.
06 · Verification

Crowdsourced verification payload.

Run this from a host that can reach the SiYuan service, ideally the target host itself or an auditor workstation on the same network. Invoke it as SIYUAN_TOKEN='your-token-if-needed' ./check-siyuan-cve-2025-21609.sh http://127.0.0.1:6806; it needs only network access to the API endpoint and no elevated privileges.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# check-siyuan-cve-2025-21609.sh
# Purpose: determine whether a SiYuan instance is vulnerable to CVE-2025-21609
# Logic: query /api/system/version and compare against the known affected version 3.1.18
# Output: VULNERABLE / PATCHED / UNKNOWN
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN

set -u

BASE_URL="${1:-http://127.0.0.1:6806}"
TOKEN="${SIYUAN_TOKEN:-}"
ENDPOINT="${BASE_URL%/}/api/system/version"
TMP_BODY="$(mktemp 2>/dev/null || echo /tmp/siyuan-cve-2025-21609.$$)"
trap 'rm -f "$TMP_BODY" >/dev/null 2>&1 || true' EXIT

if ! command -v curl >/dev/null 2>&1; then
  echo "UNKNOWN - curl not found"
  exit 2
fi

HDRS=(-H "Content-Type: application/json")
if [ -n "$TOKEN" ]; then
  HDRS+=(-H "Authorization: Token $TOKEN")
fi

HTTP_CODE="$(curl -sS -m 10 -o "$TMP_BODY" -w "%{http_code}" -X POST "${HDRS[@]}" "$ENDPOINT" -d '{}' 2>/dev/null || true)"

if [ -z "$HTTP_CODE" ] || [ "$HTTP_CODE" = "000" ]; then
  echo "UNKNOWN - unable to reach $ENDPOINT"
  exit 2
fi

BODY="$(cat "$TMP_BODY" 2>/dev/null || true)"
VERSION="$(printf '%s' "$BODY" | sed -n 's/.*"data"[[:space:]]*:[[:space:]]*"\([^"]\+\)".*/\1/p' | head -n1)"
CODE_FIELD="$(printf '%s' "$BODY" | sed -n 's/.*"code"[[:space:]]*:[[:space:]]*\([0-9-]\+\).*/\1/p' | head -n1)"

if [ -z "$VERSION" ]; then
  echo "UNKNOWN - could not parse version from response (HTTP $HTTP_CODE)"
  exit 2
fi

case "$VERSION" in
  3.1.18)
    echo "VULNERABLE - SiYuan version $VERSION matches the affected release for CVE-2025-21609"
    exit 1
    ;;
  3.1.19|3.1.20|3.1.*|3.2.*|3.3.*|3.4.*|3.5.*|3.6.*|4.*)
    echo "PATCHED - SiYuan version $VERSION is not the affected release"
    exit 0
    ;;
  *)
    if [ "$CODE_FIELD" != "0" ] && [ -n "$CODE_FIELD" ]; then
      echo "UNKNOWN - API returned code=$CODE_FIELD and version=$VERSION"
      exit 2
    fi
    echo "UNKNOWN - unrecognized version format: $VERSION"
    exit 2
    ;;
 esac
07 · Bottom Line

If you remember one thing.

TL;DR
Monday morning, do a quick inventory for SiYuan 3.1.18 and specifically identify any instance with port 6806 exposed directly or through a reverse proxy. For this HIGH verdict, use the noisgate mitigation SLA to remove public reachability and enforce access controls within 30 days, then use the noisgate remediation SLA to move every remaining 3.1.18 instance to 3.1.19 or later within 180 days. If you find an Internet-facing instance, do not wait for the full window—treat that subset as your immediate priority.

Sources

  1. NVD CVE-2025-21609
  2. GitHub Security Advisory GHSA-8fx8-pffw-w498
  3. Fix commit d9887ae
  4. Official SiYuan API documentation
  5. Official SiYuan repository / Docker deployment docs
  6. CISA Known Exploited Vulnerabilities Catalog
  7. FIRST EPSS
  8. SUSE CVE page
Peer Review

What defenders are saying.

Submit a review attribution: handle + country only
0 flags selected · stored anonymously
Validation Results

Crowdsourced verification outputs.

Results submitted by users who ran the verification payload against their environment.