siyuan-note/siyuan · error
ErrBootAppearanceAssetForbidden
ErrBootAppearanceAssetForbidden
Error message
boot appearance asset forbidden
What it means
A boot appearance asset request was refused because the requested provider/appearance (or file path) does not match the currently validated selection, or the resolved path escapes the appearance directory. ResolveBootAppearanceAsset rejects requests whose provider or appearance id differs from the persisted selection (boot_appearance.go:264), and validateBootAppearanceResource returns it when filepath.Rel indicates a path outside the appearance directory (path traversal, boot_appearance.go:526). This is a security boundary protecting against serving arbitrary kernel files.
Source
Thrown at kernel/model/boot_appearance.go:59
bootAppearanceSchemaVersion = 1
bootAppearanceDirName = "boot-appearances"
bootAppearanceConfigName = "boot-appearance.json"
bootAppearanceManifestName = "boot.json"
maxBootAppearanceManifestSize = 200 * 1024
maxBootAppearanceStyleSize = 200 * 1024
maxBootAppearanceImageSize = 5 * 1024 * 1024
maxBootAppearanceVideoSize = 20 * 1024 * 1024
maxBootAppearanceTotalSize = 50 * 1024 * 1024
maxBootAppearanceLayers = 8
maxBootAppearanceEntries = 256
maxBootAppearancePathDepth = 16
maxBootAppearancePathLength = 512
)
var (
ErrBootAppearanceNotFound = errors.New("boot appearance not found")
ErrBootAppearanceAssetForbidden = errors.New("boot appearance asset forbidden")
bootAppearanceIDPattern = regexp.MustCompile(`^[a-z0-9]+(?:-[a-z0-9]+)*$`)
bootAppearanceColorPattern = regexp.MustCompile(`^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$`)
bootAppearanceConfLock sync.RWMutex
)
// BootAppearanceSelection 表示当前工作空间选择的启动页外观。
type BootAppearanceSelection struct {
SchemaVersion int `json:"schemaVersion"`
Provider string `json:"provider"`
Appearance string `json:"appearance"`
}
// BootAppearance 描述已经校验且可安全交给启动页渲染的外观。
type BootAppearance struct {
Enabled bool `json:"enabled"`
Provider string `json:"provider,omitempty"`
Appearance string `json:"appearance,omitempty"`View on GitHub (pinned to 8641553a1f)
Solutions
- Serve/request assets only for the currently selected provider and appearance (re-read the selection and regenerate asset URLs).
- Remove path traversal sequences and use only relative asset paths within the package.
- Replace or remove symlinks in the appearance package that point outside its directory.
- Reload the boot page after changing the selection so cached old-provider asset URLs are not used.
Example fix
// before
resolve("/themes/other-provider/assets/logo.png") // provider mismatch
// after
sel := model.ResolveBootAppearanceSelection()
asset, err := model.ResolveBootAppearanceAsset(sel.Provider, sel.Appearance, "assets/logo.png") Defensive patterns
Strategy: validation
Validate before calling
function assetPathIsSafe(p) { return !p.includes('..') && !path.isAbsolute(p) } Try / catch
asset, err := model.ResolveBootAppearanceAsset(provider, id, rel)
if errors.Is(err, model.ErrBootAppearanceAssetForbidden) {
http.Error(w, "forbidden", http.StatusForbidden)
} Prevention
- Always derive asset URLs from the current persisted selection
- Reject or normalize paths containing traversal segments before requesting
- Avoid symlinks inside appearance packages
When it happens
Trigger: Requesting a boot appearance asset URL with a provider or appearance id that differs from the currently persisted selection; requesting asset paths containing ../ or absolute path components that escape the appearance directory; calling validateBootAppearanceResource with a symlink-resolved path outside the package directory.
Common situations: Stale frontend references assets from a previously selected appearance after the user switched selections; crafted URLs attempting directory traversal; symlinked files inside the appearance package pointing outside.
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.
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- import path is not sub path of import dir
- invalid custom emoji name
- path escapes workspace: %s
- path escapes templates dir: %s
- path [%s] must not contain '..'
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/fb13e5601c696003.
Report an issue: GitHub.