initial commit
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
# Security model
|
||||
|
||||
LiveWall is a wallpaper app that can run third-party code. This page states plainly what that means,
|
||||
what the app does about it, and what it cannot do.
|
||||
|
||||
## The blunt version
|
||||
|
||||
A plugin is a .NET assembly loaded **into LiveWall's own process**, with LiveWall's rights. There is
|
||||
no sandbox, no separate account, no permission-checking kernel layer. A plugin can:
|
||||
|
||||
* read and write any file your user account can reach (documents, browser profiles, keys);
|
||||
* open network connections and upload whatever it found;
|
||||
* change or delete your LiveWall settings and wallpapers;
|
||||
* register global hotkeys and log keystrokes of its own (as any process can);
|
||||
* crash LiveWall (only the plugin's own failures are contained — see below).
|
||||
|
||||
The permission list is therefore **informed consent, not a security boundary**. LiveWall enforces it
|
||||
where enforcement makes sense (it will not register an overlay without `DesktopOverlay`), and the
|
||||
warning dialog shows exactly what a plugin asked for, but a hostile plugin that lies in its manifest
|
||||
is only limited by your user account.
|
||||
|
||||
Install plugins the way you install any other program: from a source you trust, ideally with a
|
||||
reviewable repository.
|
||||
|
||||
## What LiveWall actually does
|
||||
|
||||
### The warning before anything runs
|
||||
|
||||
Two gates, both modal, both requiring an explicit acknowledgement:
|
||||
|
||||
1. **The plugin system itself.** Turning on *Enable the plugin system* shows a full-screen-format
|
||||
warning: third-party code, unverified plugins may cause harm to your computer, nothing is
|
||||
sandboxed, only proceed with sources you trust. The checkbox must be ticked before *Enable plugins*
|
||||
becomes clickable. `ConsentGiven` is stored, so this is asked once per installation.
|
||||
|
||||
2. **Every single plugin.** Enabling a plugin shows a card with name, author, version, id, folder,
|
||||
its **SHA-256 hash**, a verified/unverified verdict, and the full permission list expanded into
|
||||
human sentences. Again the acknowledgement must be ticked first.
|
||||
|
||||
The same warning banner stays visible at the top of the **Plugins** tab permanently — the user is
|
||||
never more than one click from the reminder.
|
||||
|
||||
### Off by default
|
||||
|
||||
`Plugins.Enabled` is `false` in a fresh configuration, and `AllowUnverified` is `false` as well. With
|
||||
both off, nothing in `%APPDATA%\LiveWall\plugins` is loaded at all; the folder is only scanned so the
|
||||
list can be shown.
|
||||
|
||||
### Hash-pinned trust
|
||||
|
||||
* Every plugin's entry assembly is hashed (SHA-256) at load time.
|
||||
* When the user accepts a warning, that exact hash is stored in
|
||||
`%APPDATA%\LiveWall\plugin-trust.json` as `{ "<plugin id>": "<sha256>" }`, and mirrored into
|
||||
`settings.json` (`Plugins.Plugins[] { Enabled, ApprovedHash }`).
|
||||
* If the DLL changes — an update, but also the first sign of tampering — the hash no longer matches,
|
||||
the plugin is **unverified** again, and the warning reappears. There is no way to enable a
|
||||
mismatched plugin silently: the *Enable* button path always shows the dialog.
|
||||
* **Forget trust** (in the row, visible for verified plugins) removes the stored hash; the next
|
||||
enable asks again.
|
||||
* A manifest may also carry a `checksum`; when present it must match the assembly or the plugin is
|
||||
rejected outright (`ChecksumMismatch`), which lets a publisher pin the exact build they shipped.
|
||||
|
||||
### Failing safely
|
||||
|
||||
* `Initialize`, every command, page build, filter pass, renderer call, hotkey action and event
|
||||
handler is wrapped: exceptions are logged with the plugin id and the offending feature stops being
|
||||
used. The plugin shows as **Failed** with the message.
|
||||
* A plugin that fails to load is never retried in a loop.
|
||||
* A crashing native plugin can still take the process down (as can any in-process code). LiveWall
|
||||
logs each step of loading so the culprit is identifiable afterwards.
|
||||
* LiveWall never executes a plugin it has not been explicitly told to enable — there is no
|
||||
auto-load from a zip, download, or command line.
|
||||
|
||||
### Isolation of the file system
|
||||
|
||||
* Plugin data goes to `%APPDATA%\LiveWall\plugin-data\<id>\` (`IPluginStorage`). Folder names are
|
||||
sanitised from the plugin id, so `../../Windows` cannot be used to escape.
|
||||
* The plugin folder itself is `%APPDATA%\LiveWall\plugins\<folder>\`. Nothing else in the app's own
|
||||
data folder is exposed through the SDK except `Storage.PathFor`, which is confined to the plugin's
|
||||
data folder.
|
||||
|
||||
### Assembly loading
|
||||
|
||||
Plugins are loaded into a **collectible `AssemblyLoadContext`** that has `LiveWall.Sdk` shared with
|
||||
the host. This means:
|
||||
|
||||
* two plugins can ship different versions of the same helper library without colliding;
|
||||
* unloading actually releases the assembly, so a fixed plugin can be retried after **Rescan**;
|
||||
* a plugin cannot accidentally shadow `LiveWall.Sdk` and hand back the wrong interface type.
|
||||
|
||||
## What LiveWall does *not* do
|
||||
|
||||
* It does not sandbox, virtualise or restrict the plugin's file/network/process access.
|
||||
* It does not scan plugins for malware, and does not transmit hashes anywhere.
|
||||
* It does not "verify" against a public signature: **verified** on this machine means *"this exact
|
||||
file is the one you approved"*, not *"this file is trustworthy"*.
|
||||
* It does not protect against a plugin that persists (auto-start entry, scheduled task) — check
|
||||
`shell:startup`, `HKCU\Software\Microsoft\Windows\CurrentVersion\Run` and Task Scheduler if you
|
||||
distrust something you ran.
|
||||
* It cannot undo what a plugin already did to your files. Back up first if you are experimenting.
|
||||
|
||||
## Advice for users
|
||||
|
||||
1. Turn the plugin system on only when you have a plugin you actually want.
|
||||
2. Read the permission list. `FileSystem` + `Network` together is "it can upload my files" — that had
|
||||
better be a plugin you trust.
|
||||
3. Prefer plugins whose source you can read. A DLL alone is a black box.
|
||||
4. If the hash of a plugin you have been using changes after an update, that is expected — but it is
|
||||
also exactly what tampering looks like, so treat the new warning as real.
|
||||
5. To be sure a plugin is gone: **Disable** it, delete its folder, and delete
|
||||
`%APPDATA%\LiveWall\plugin-data\<id>`.
|
||||
|
||||
## Advice for developers
|
||||
|
||||
* Declare **all** permissions you use, and nothing else. Over-declaring trains users to click through.
|
||||
* Never call `SetWallpaper` in a loop; it reloads media.
|
||||
* Don't write outside `context.Storage.DataFolder`. If you need a user-chosen location, ask for it
|
||||
explicitly and remember it.
|
||||
* Long work belongs on the thread pool; the UI thread stalls the wallpaper and your own overlay.
|
||||
* Log through `context.Log`, not to your own file, so everything is in one place for support.
|
||||
* Treat the returned `IDisposable`s and `ShutdownToken` seriously: unload happens while the user is
|
||||
watching, and a plugin that leaves a timer running keeps the load context alive.
|
||||
Reference in New Issue
Block a user