Skip to content

Plugin Trust Model

60plus edited this page Jul 11, 2026 · 2 revisions

Plugin Trust Model

GamesDownloader plugins are not sandboxed scripts or a limited macro language. A plugin is ordinary Python code that GamesDownloader imports and runs inside its own process. This page explains, plainly, what that means for security, why the design is deliberate rather than a flaw, and how to stay safe.

The short version: installing a plugin is equivalent to running arbitrary software on your server. Treat it exactly as you would treat any other program you choose to run.

Contents

What a plugin actually is

When GamesDownloader loads a plugin, it locates the plugin's entry file and executes it as a module (using Python's importlib machinery to build a spec from the file location and then run the module). That code runs in the same process as the application, so it inherits everything the application can do:

A loaded plugin can reach Because
The database plugins are given a synchronous helper that connects using the app's own database URL, and nothing stops a plugin opening its own connection
The filesystem it runs as the same OS user as the server, so it sees every file the container/process can read or write, including game files, ROMs, downloads, and config
Environment variables it shares the process environment, which includes secrets such as database credentials and the auth signing key
The network it can make outbound requests to anywhere the server can reach, including other machines on your LAN
Application internals it can import backend modules, call handlers, and register hooks that run on real requests

A plugin's declared Python dependencies (a requirements.txt inside the ZIP) are also installed and imported into the process, so any code in those dependencies runs with the same reach.

In one sentence: a plugin has the same privileges as GamesDownloader itself. There is no privilege boundary between the two.

This is by design, not a vulnerability

This is the intended extension model, and it is the same trade-off every extensible application makes:

  • A browser extension can read the pages you visit and your cookies.
  • An IDE or editor plugin runs code with your user account on your machine.
  • A CMS plugin (WordPress, etc.) runs PHP inside the site with database access.

None of these are considered bugs. They are the price of a rich extension system: to be genuinely useful, an extension has to be able to do things, and drawing a perfect sandbox around arbitrary third-party code while still letting it add metadata providers, download sources, themes, and widgets is not realistically achievable. GamesDownloader makes the same choice on purpose. The value of the plugin and theme ecosystem (see Plugin Development) depends on plugins being able to reach the database, the library, and the network.

So "a plugin can run any code" is a documented property of the system, not a hole to be reported.

Why this is acceptable in practice

The reason this is safe in normal operation is that installing a plugin is an administrator-only action, and an administrator is already the trust root of the instance.

Plugin installation and management endpoints are gated by a plugins.write permission scope, which is granted only to the admin role. Ordinary users, editors, and uploaders cannot install, enable, configure, or remove plugins.

An admin can already:

  • read and change every setting,
  • reach the database and the underlying host,
  • add or remove users and grant admin rights.

An admin who installs a malicious plugin has therefore not crossed any new boundary - they already had full control. Conversely, a plugin only becomes a threat if the admin account itself is compromised, and at that point an attacker controls the whole instance regardless of plugins. There is no scenario where a non-admin gains extra power through the plugin system, because they cannot install plugins in the first place.

The real security boundary is admin login, not the plugin loader. Everything on this page reduces to: protect the admin account. See Security.

The guardrails that do exist

The plugin system is not a free-for-all. Several controls narrow the ways a plugin can arrive and how much can go wrong by accident, even though none of them sandbox the code once it runs:

Control What it does
Admin-only installation install/enable/disable/remove all require the admin-only plugins.write scope
Admin login protection admin authentication sits behind Redis brute-force rate limiting and optional two-factor authentication, so the boundary that matters is hardened
ZIP-only installation plugins are installed by uploading a ZIP (or fetching one from a store source); there is no arbitrary "point it at a package name" path
No setuptools entry points automatic discovery of installed pip packages as plugins is deliberately disabled, to avoid a supply-chain path where an unrelated package silently registers itself as a plugin
Zip Slip protection archive extraction rejects absolute paths and .. components, and normalises separators, so a plugin cannot write files outside its own directory during install
Path-traversal guard on entry (startup scan) during the startup directory scan the manifest's entry field is rejected if it contains .., /, or \, and the resolved path must stay inside the plugin directory. This check runs only on the startup scan, not on the runtime install/enable path (which relies on the Zip Slip protection above and the plugin-id check below instead)
Plugin id validation the plugin id may not contain path separators or .., keeping each plugin confined to its own folder
min_gd_version gate a plugin declaring a newer minimum GamesDownloader version than the running server is refused, so plugins run only against a version they were built for
Store install allowlist one-click store installs will only download from github.com (and related GitHub hosts) or a store source you have explicitly configured, not from an arbitrary attacker-supplied URL

What the guardrails are not

To set expectations correctly:

  • They do not sandbox plugin code. Once a plugin loads, it runs with full application privileges as described above.
  • The path-traversal and Zip Slip checks protect where files land and which file is executed. They do not restrict what the executed code then does.
  • The min_gd_version gate is a compatibility check, not a safety review. It says nothing about whether the plugin is trustworthy.
  • The store allowlist limits where a package is fetched from, not what is inside it. A malicious ZIP hosted on an allowlisted source is still malicious.

In other words, the guardrails reduce accidental damage and shrink the attack surface for reaching the loader. The decision about whether the code is trustworthy is yours.

Guidance for administrators

Because installing a plugin runs its author's code on your server, apply the same judgement you would for any software you install:

  • Only install plugins from sources you trust. Prefer the official template and store repositories and authors you recognise. An unknown ZIP from an unknown source is unknown code.
  • Read what you can. Plugins are plain Python and assets; if you can review the source before installing, do.
  • Keep the admin account locked down. Use a strong, unique password, enable two-factor authentication, and do not share admin credentials. This is the boundary that actually protects you.
  • Prefer LAN-only or trusted-network exposure for admin access where possible, and review the hardening advice on the Security and Network and Security pages.
  • Remove plugins you no longer use. Fewer installed plugins means less third-party code running in your process.

Rule of thumb: if you would not run a random program from this author on your server as a privileged user, do not install their plugin either. It is the same decision.

Next: Plugin Development - Security

Clone this wiki locally