Skip to content

Security: Flowfin/jellyfin-plugin-requests

SECURITY.md

Reporting a security problem

The private route

Use GitHub's private vulnerability reporting for this repository: the Security tab, then Report a vulnerability. It is enabled here, and it opens a draft advisory only you and I can read.

The form is here, without navigating:

https://github.com/Flowfin/jellyfin-plugin-requests/security/advisories/new

That is the route. Do not open a public issue for a security problem, and do not put one in a pull request body: both are readable by anybody the moment they are written, including by whoever would use what you found.

If you cannot reach that form, open a public issue saying only that you have something to report privately and nothing about what it is, and you will be given a route.

What to expect back

You will get a reply, from one person. This repository is maintained by one person in their own time; there is no rota behind it and no response time is promised here, because a promise nobody is staffed to keep is worse than an honest silence about it.

What is promised is the shape of the reply, in this order:

  • an acknowledgement that the report was read, and whether it is understood;
  • a verdict, with the reasoning: whether it is a problem in this plugin, in something it depends on, or not a problem, and where the line was drawn;
  • where it is a problem, what the fix is and when it lands, and where it is not, why not.

Credit is offered by default and refused on request. A fix names what was fixed and what it prevented, in the changelog, whether or not the reporter is named.

What is in scope

This plugin, in this repository: the code, the packaging metadata, the workflows, and anything in the tree.

What is not, and where it goes instead:

  • Jellyfin itself, including its authentication, its API and its dashboard. Those go to the Jellyfin project.
  • The server this plugin is installed on. How it is exposed, what is in front of it and who can reach it are the operator's, and this plugin makes none of those choices.
  • An external request service this plugin can be pointed at. That is a separate product with its own reporting route.

What this plugin holds

A report is easier to write against a plugin whose data you know. This one keeps requests: who asked, what they asked for, when, the decisions made on it and by whom, and the notes people wrote. People are recorded by the server's own user identifier and never by name.

There is no telemetry. Nothing about use is collected and nothing is sent anywhere for this project's benefit, without exception and without a later review of that.

The plugin makes two kinds of outbound call and an operator has to turn each on. One is the notification sink and the other is the bridge to an external request service, and the six lines below are two paths rather than six: a client each, and the socket pipeline each is handed at registration.

git grep -nE 'HttpClient|WebRequest|WebSocket|Socket' -- Jellyfin.Plugin.Requests
Jellyfin.Plugin.Requests/Bridge/Overseerr/OverseerrBackend.cs:124:    private readonly HttpClient _client;
Jellyfin.Plugin.Requests/Bridge/Overseerr/OverseerrBackend.cs:166:        _client = new HttpClient(handler, disposeHandler: false)
Jellyfin.Plugin.Requests/Notify/OutboundSink.cs:61:    private readonly HttpClient _client;
Jellyfin.Plugin.Requests/Notify/OutboundSink.cs:104:        _client = new HttpClient(handler, disposeHandler: false)
Jellyfin.Plugin.Requests/PluginServiceRegistrator.cs:95:            new SocketsHttpHandler(),
Jellyfin.Plugin.Requests/PluginServiceRegistrator.cs:157:            new SocketsHttpHandler(),

The sink posts nothing until an operator sets OutboundNoticeAddress, and the bridge sends nothing until an operator sets BridgeAddress; both are empty on every install where nobody has decided otherwise, and there is no other way to turn either on. What the sink posts is one small document per movement in the queue, and what the bridge sends is one submission per approval and one question per handed-over request on a schedule. docs/notifications.md writes the sink's document out field by field, docs/bridge.md does the same for the submission, and docs/personal-data.md is the account of everything that leaves the server.

The bridge holds a credential, BridgeApiKey, and it is the one value in this plugin's configuration that authenticates anything. It travels in a header to the configured address and nowhere else, it is never written to a log or shown back on the settings page, and it is stored in the plugin's settings file with the protection the server's data directory gets and no more; that claim, who on the machine can read the file, and what is refused by name are in docs/bridge.md.

This section said the plugin makes no outbound call at all, and that stopped being true on 2026-08-16. The sentence is corrected here rather than quietly replaced, because somebody who read it and took it for a property of the plugin took it from this page. It was never a change in behaviour: the sink landed with its switch off and has never been on by default. What was wrong is that this page went on asserting the tree of six days earlier, and the pasted command under it went on being quoted with an output it no longer produced.

This page then said a bridge to an external request service would be a second such path and did not exist yet, and that stopped being true with #315. The paragraph above is the corrected one, for the same reason as the correction before it: somebody who read the sentence took it from this page.

There aren't any published security advisories