siyuan-note/siyuan · error
invalid encrypted asset original name
Error message
invalid encrypted asset original name
What it means
decryptAssetMetadata validates metadata.OriginalName: it must be non-empty, not ".", equal to its own filepath.Base (no directory components), and contain no '/' or '\\'. This prevents path traversal and directory escapes when the original name is restored to disk.
Source
Thrown at kernel/model/crypto.go:2348
if err := json.Unmarshal(data, metadata); err != nil {
return nil, err
}
var version struct {
Spec json.RawMessage `json:"spec"`
ContainerID json.RawMessage `json:"containerID"`
}
if err := json.Unmarshal(data, &version); err != nil {
return nil, err
}
// 仅认证元数据同时缺少两个版本字段时按旧容器读取,显式空值或不完整的新格式不能降级。
if len(version.Spec) == 0 && len(version.ContainerID) == 0 {
metadata.Spec = encryptedAssetLegacySpec
} else if metadata.Spec != encryptedAssetSpec || len(metadata.ContainerID) != encryptedAssetContainerIDSize {
return nil, errors.New("unsupported encrypted asset container version")
}
if metadata.OriginalName == "" || metadata.OriginalName == "." ||
filepath.Base(metadata.OriginalName) != metadata.OriginalName || strings.ContainsAny(metadata.OriginalName, `/\`) {
return nil, errors.New("invalid encrypted asset original name")
}
if metadata.Size < 0 {
return nil, errors.New("invalid encrypted asset content metadata")
}
chunks := uint64(metadata.Size) / encryptedAssetChunkSize
if metadata.Size%encryptedAssetChunkSize != 0 || metadata.Size == 0 {
chunks++
}
if metadata.Chunks != chunks {
return nil, errors.New("invalid encrypted asset chunk count")
}
return metadata, nil
}
// DecryptAssetWithName 解密资源内容并返回原始名称。
func DecryptAssetWithName(boxID, diskName string, dek, ciphertext []byte) (plaintext []byte, originalName string, err error) {
var output bytes.Buffer
originalName, err = DecryptAssetToWriter(boxID, diskName, dek, bytes.NewReader(ciphertext), &output)View on GitHub (pinned to 8641553a1f)
Solutions
- Re-encrypt the asset with a sanitized original name (base name only, no separators)
- If the container is untrusted, do not decrypt-and-restore it; treat it as hostile input
- Check the encryption-side code path that built encryptedAssetMetadata to ensure it passes filepath.Base of the real name
- Restore the asset from a trusted backup if metadata was corrupted
Example fix
// before: encrypt with a user-supplied path as name meta.OriginalName = userPath // after: store only the base name meta.OriginalName = filepath.Base(userPath)
Defensive patterns
Strategy: validation
Validate before calling
func safeAssetName(name string) bool {
return name != "" && name != "." && filepath.Base(name) == name && !strings.ContainsAny(name, `/\`)
} Try / catch
if err != nil && strings.Contains(err.Error(), "original name") {
// reject the container as untrusted/tampered
} Prevention
- Always store filepath.Base of the original name in metadata
- Treat containers from untrusted sources as hostile; never restore names verbatim
- Sanitize names at import time, before encryption
When it happens
Trigger: Decrypting an asset whose metadata original name was crafted with '../', absolute paths, embedded separators, or is empty — typically a tampered container or a bug in code that populated metadata before encryption.
Common situations: Assets imported by third-party tooling that wrote unsafe names into metadata, malicious containers crafted to overwrite files outside the assets dir, corruption flipping bytes in the name field.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
- asset path is outside data directory
- asset path escapes data directory: %s
- encrypted asset metadata is too large
- invalid encrypted asset format
- invalid encrypted asset metadata size
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/dcb5d4cdc77b5b79.
Report an issue: GitHub.