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

  1. Serve/request assets only for the currently selected provider and appearance (re-read the selection and regenerate asset URLs).
  2. Remove path traversal sequences and use only relative asset paths within the package.
  3. Replace or remove symlinks in the appearance package that point outside its directory.
  4. 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

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.

Related errors


AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11). Data as JSON: /api/errors/fb13e5601c696003. Report an issue: GitHub.