Skip to content

Go net/http/pprof debug endpoints, including heap dumps, are registered fully unauthenticated whenever --mode is not exactly "prod", exposing in-memory secrets (AI provider API keys, AccessAuthCode) with no corresponding warning on the flag ```

Critical
88250 published GHSA-9cqq-p2hw-mj3f Aug 3, 2026

Package

gomod github.com/siyuan-note/siyuan/kernel (Go)

Affected versions

3.7.3

Patched versions

v3.7.4

Description

Summary

serveDebug() registers Go's standard net/http/pprof endpoints,
including /debug/pprof/heap and /debug/pprof/goroutine, directly on the
same gin engine that serves the main API, with no authentication
middleware of any kind. This is guarded by a single check,
if "prod" == util.Mode { return }, so it is disabled by default in
release builds. However, util.Mode is set by a documented, user-facing
CLI flag on the serve command, --mode, whose help text reads simply
"dev/prod" with no indication that the non-default value exposes raw
process memory to the network with zero authentication. A heap or
goroutine dump of a running SiYuan kernel can contain the workspace's
AccessAuthCode and any configured AI provider API keys, all stored as
plain Go strings in the in-memory Conf structure, alongside arbitrary
note content and other session state. This is CWE-215 (Information
Exposure Through Debug Information) and CWE-497 (Exposure of Sensitive
System Information to an Unauthorized Control Sphere), with CWE-400
(Uncontrolled Resource Consumption) as a secondary concern via
/debug/pprof/profile//debug/pprof/trace, which can be made to
block/consume CPU for an attacker-chosen duration.

Details

kernel/server/serve.go, serveDebug():

func serveDebug(ginServer *gin.Engine) {
    if "prod" == util.Mode {
        // The production environment will no longer register `/debug/pprof/` https://github.com/siyuan-note/siyuan/issues/10152
        return
    }

    ginServer.GET("/debug/pprof/", gin.WrapF(pprof.Index))
    ginServer.GET("/debug/pprof/allocs", gin.WrapF(pprof.Index))
    ginServer.GET("/debug/pprof/block", gin.WrapF(pprof.Index))
    ginServer.GET("/debug/pprof/goroutine", gin.WrapF(pprof.Index))
    ginServer.GET("/debug/pprof/heap", gin.WrapF(pprof.Index))
    ginServer.GET("/debug/pprof/mutex", gin.WrapF(pprof.Index))
    ginServer.GET("/debug/pprof/threadcreate", gin.WrapF(pprof.Index))
    ginServer.GET("/debug/pprof/cmdline", gin.WrapF(pprof.Cmdline))
    ginServer.GET("/debug/pprof/profile", gin.WrapF(pprof.Profile))
    ginServer.GET("/debug/pprof/symbol", gin.WrapF(pprof.Symbol))
    ginServer.GET("/debug/pprof/trace", gin.WrapF(pprof.Trace))
}

None of these routes pass model.CheckAuth or any other middleware, unlike
every API route in the application.

util.Mode (kernel/util/working.go:45) defaults to "prod"
(var Mode = "prod"), and the referenced issue #10152 shows the
maintainers already fixed the "always registered" version of this bug
once. The remaining exposure is entirely through the documented --mode
flag on the serve CLI command (kernel/cli/cmd/serve.go:106):

serveCmd.Flags().StringVar(&serveMode, "mode", "prod", "dev/prod")

This is ordinary, discoverable CLI surface (kernel serve --help shows
it), not a hidden debug backdoor, and nothing in its help text or
documentation conveys that choosing dev disables authentication on a
process-memory-dumping endpoint. This is exactly the kind of flag an
operator might reasonably pass while troubleshooting a self-hosted
instance, including a network-exposed one, without realizing the security
implication. The check itself is also a strict-equality allowlist of one
string ("prod"); any other value, including a future additional mode,
a typo, or "production" instead of "prod", silently falls through to
registering these routes.

Secrets confirmed present as plain in-memory strings and therefore
recoverable from a heap dump:

  • Conf.AccessAuthCode (the workspace lock-screen code covered elsewhere
    in this review)
  • kernel/conf/ai.go: multiple APIKey string fields across AI provider
    configuration structs

PoC

# Start SiYuan with the documented dev flag, e.g.:
#   kernel serve --mode dev --workspace <path>
# Then, with no credentials of any kind:
curl -s http://<target>:6806/debug/pprof/heap -o heap.out
curl -s http://<target>:6806/debug/pprof/goroutine?debug=2
curl -s http://<target>:6806/debug/pprof/cmdline
# heap.out can be searched/analyzed offline (e.g. with `go tool pprof` or
# simple string extraction) for API keys, the access code, and note
# content residing in memory at dump time.

# Resource-exhaustion angle, no special flags needed once exposed:
curl -s "http://<target>:6806/debug/pprof/profile?seconds=60" &
# repeat concurrently to tie up CPU/goroutines for the requested duration

(Not run against a live compiled kernel, same sandbox limitation as prior
findings in this round; the routing/guard logic and the CLI flag default
are read directly from the three locations quoted above, and
net/http/pprof's content, full heap/goroutine memory dumps with no
redaction, is standard, well-documented Go standard library behavior.)

Impact

Any SiYuan deployment started with --mode dev (or any value other than
exactly "prod") on a network-reachable interface exposes complete,
unauthenticated memory-dump and CPU-profiling endpoints. A single request
to /debug/pprof/heap can recover the workspace's AccessAuthCode
(defeating every other authentication control examined in this review) and
any configured AI provider API keys, with no session, cookie, or
credential required at all. This is a well-known, frequently-exploited
real-world vulnerability class for Go services in general; the specific
concern here is that SiYuan exposes it through a documented, easily-typed
CLI flag with no warning that "dev mode" carries this consequence, making
accidental exposure during routine troubleshooting plausible even for
operators who are otherwise careful about the application's other
authentication settings.


## Affected products

| Field | Value |
|---|---|
| Ecosystem | **Go** |
| Package name | `github.com/siyuan-note/siyuan/kernel` |
| Affected versions | `<= 3.7.3` (confirmed present in 3.7.3; conditional on `--mode` not equal to `"prod"`, which is not the default) |
| Patched versions | *(none yet, leave blank until a fix is released)* |

## Severity

| Field | Value |
|---|---|
| Vector string | `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:L` |
| Score | **9.1 (Critical)** *if* the non-default `--mode` value is in effect (network vector, no privileges or interaction needed, scope change since recovered secrets grant access far beyond this endpoint itself, high confidentiality impact, some availability impact via the profiling endpoints). Given this requires a non-default runtime flag, maintainers may reasonably prefer scoring this as environmental/conditional rather than a flat 9.1; recommend presenting both the "if triggered" severity and the precondition clearly in the advisory, exactly as done here, rather than picking one number that misrepresents either the ceiling or the default-safe floor. |

## Weaknesses (CWE)

- **CWE-215**: Information Exposure Through Debug Information (primary)
- **CWE-497**: Exposure of Sensitive System Information to an Unauthorized Control Sphere
- **CWE-400**: Uncontrolled Resource Consumption (via `/debug/pprof/profile` and `/debug/pprof/trace`)

## Notes for filing
- Genuinely distinct root cause from the `/public/` authentication
  advisory (different code path, different trigger condition, different
  consequence, direct secret disclosure vs. content disclosure), safe to
  file separately, though both share the broader theme from this review of
  routes that fall outside the application's otherwise-consistent
  `CheckAuth` enforcement.
- Suggested fix direction: at minimum, update the `--mode` flag's help
  text and documentation to explicitly warn that `dev` mode exposes
  unauthenticated process-memory-dump endpoints and should never be used
  on a network-reachable instance. More robustly, gate `serveDebug()`
  behind its own explicit opt-in flag (e.g. `--enable-pprof`) separate
  from the general dev/prod mode, and/or require `model.CheckAuth` +
  `model.CheckAdminRole` on these routes the same as every other
  sensitive endpoint in the application, rather than relying solely on a
  string-equality check against one deployment mode.

Severity

Critical

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
None
Scope
Changed
Confidentiality
High
Integrity
None
Availability
Low

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:N/S:C/C:H/I:N/A:L

CVE ID

No known CVE

Weaknesses

Insertion of Sensitive Information Into Debugging Code

The product inserts sensitive information into debugging code, which could expose this information if the debugging code is not disabled in production. Learn more on MITRE.

Uncontrolled Resource Consumption

The product does not properly control the allocation and maintenance of a limited resource. Learn more on MITRE.

Exposure of Sensitive System Information to an Unauthorized Control Sphere

The product does not properly prevent sensitive system-level information from being accessed by unauthorized actors who do not have the same level of access to the underlying system as the product does. Learn more on MITRE.

Credits