siyuan-note/siyuan · error
boot appearance asset forbidden
Error message
boot appearance asset forbidden
What it means
ErrBootAppearanceAssetForbidden guards serving boot appearance asset files. ResolveBootAppearanceAsset refuses paths whose provider/appearance don't match the persisted selection, and validateBootAppearanceResource rejects files that escape the appearance directory (filepath.Rel fails or the relative path leaves the root) — blocking path traversal and cross-plugin asset access.
Solutions
- Only request assets under the currently selected provider/appearance (re-resolve after any selection change)
- Ensure request paths are relative and contain no '..' or leading '/' components
- Remove or avoid symlinks escaping the appearance directory
- Clear stale selection state via SetBootAppearance("") so resolution matches the assets being served
Example fix
// before
url := "/boot-appearance/other-plugin/other-appearance/logo.svg" // mismatch or traversal
// after
sel := model.GetSelectedBootAppearance()
url := fmt.Sprintf("/boot-appearance/%s/%s/logo.svg", sel.Provider, sel.Appearance) Defensive patterns
Strategy: validation
Validate before calling
clean := func(p string) bool { return p == path.Clean("/"+p)[1:] && !strings.Contains(p, "..") }
if !clean(assetPath) { return errors.New("illegal asset path") } Type guard
func safeRelPath(p string) bool {
rel, err := filepath.Rel(base, filepath.Join(base, p)); return err == nil && rel == p && !strings.HasPrefix(rel, "..")
} Try / catch
data, err := model.ResolveBootAppearanceAsset(p, id, rel)
if errors.Is(err, model.ErrBootAppearanceAssetForbidden) {
http.Error(w, "forbidden", http.StatusForbidden)
} Prevention
- Only use asset URLs derived from the current selection
- Reject '..' and absolute segments in user-supplied paths
- Avoid symlinks inside appearance directories
- Regenerate asset URLs after any selection change
When it happens
Trigger: Requesting a boot appearance asset for a plugin/appearance that is not the currently selected one (line 264); requesting a path that resolves outside the appearance directory via ../ traversal or symlinks (line 526).
Common situations: Crafted or stale URLs pointing at another plugin's assets; paths containing '..' segments or absolute components; symlinks inside an appearance dir pointing outward; serving cached asset URLs after the selection changed.
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
- boot appearance not found
- ErrBootAppearanceAssetForbidden
- invalid appearance ID
- invalid boot appearance selection
- invalid child template path
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/f42c92aea9a39d40.
Report an issue: GitHub.
Appendix: 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 9f775e8a12)