1
0
Fork 0
mirror of https://github.com/jellyfin/jellyfin-web synced 2025-03-30 19:56:21 +00:00
jellyfin-web/src/controllers/dashboard/plugins/add/index.js
Joshua M. Boniface 509cbabedb Add confirmation for 3rd party repos
Adds a confirmation similar to the one performed during plugin
installation, when adding a 3rd party repository.

The safe domain is hardcoded to be "repo.jellyfin.org" as this is very
stable and we have no plans to change it. Individual mirrors don't need
to be specified since this is user-input content and they should be
using the main URL not the URL of a specific mirror.

The confirmation message makes explicit mention of the possibility of
malicious code from 3rd party repositories as well as updates that may
bring it in, and suggests only adding 3rd parties from trusted people.

The plugin install confirmation is also modified to use the same
conditional and an altered message similar to the above, again to
emphasize the potential security risks of 3rd party plugins.

Finally, some additional information is added to the Developer Info
section of the plugin page; specifically, the name of the repository the
plugin is sourced from as well as its URL. How this is obtained is a
hack, since these should probably be part of the main information about
the plugin and not each specific version, but this is worked around by
only showing the information from the first (i.e. newest) version.
2023-05-29 10:59:21 -04:00

161 lines
5.7 KiB
JavaScript

import 'jquery';
import { marked } from 'marked';
import DOMPurify from 'dompurify';
import loading from '../../../../components/loading/loading';
import globalize from '../../../../scripts/globalize';
import '../../../../elements/emby-button/emby-button';
import Dashboard from '../../../../scripts/clientUtils';
import alert from '../../../../components/alert';
import confirm from '../../../../components/confirm/confirm';
function populateHistory(packageInfo, page) {
let html = '';
const length = Math.min(packageInfo.versions.length, 10);
for (let i = 0; i < length; i++) {
const version = packageInfo.versions[i];
html += '<h2 style="margin:.5em 0;">' + version.version + '</h2>';
html += '<div style="margin-bottom:1.5em;">' + DOMPurify.sanitize(marked(version.changelog)) + '</div>';
}
$('#revisionHistory', page).html(html);
}
function populateVersions(packageInfo, page, installedPlugin) {
let html = '';
packageInfo.versions.sort((a, b) => {
return b.timestamp < a.timestamp ? -1 : 1;
});
for (let i = 0; i < packageInfo.versions.length; i++) {
const version = packageInfo.versions[i];
html += '<option value="' + version.version + '">' + globalize.translate('PluginFromRepo', version.version, version.repositoryName) + '</option>';
}
const selectmenu = $('#selectVersion', page).html(html);
if (!installedPlugin) {
$('#pCurrentVersion', page).hide().html('');
}
const packageVersion = packageInfo.versions[0];
if (packageVersion) {
selectmenu.val(packageVersion.version);
}
}
function renderPackage(pkg, installedPlugins, page) {
const installedPlugin = installedPlugins.filter(function (ip) {
return ip.Name == pkg.name;
})[0];
console.log(pkg)
populateVersions(pkg, page, installedPlugin);
populateHistory(pkg, page);
$('.pluginName', page).text(pkg.name);
$('#btnInstallDiv', page).removeClass('hide');
$('#pSelectVersion', page).removeClass('hide');
if (pkg.overview) {
$('#overview', page).show().text(pkg.overview);
} else {
$('#overview', page).hide();
}
$('#description', page).text(pkg.description);
$('#developer', page).text(pkg.owner);
// This is a hack; the repository name and URL should be part of the global values
// for the plugin, not each individual version. So we just use the top (latest)
// version to get this information.
$('#repositoryName', page).text(pkg.versions[0].repositoryName);
$('#repositoryUrl', page).text(pkg.versions[0].repositoryUrl);
if (installedPlugin) {
const currentVersionText = globalize.translate('MessageYouHaveVersionInstalled', '<strong>' + installedPlugin.Version + '</strong>');
$('#pCurrentVersion', page).show().html(currentVersionText);
} else {
$('#pCurrentVersion', page).hide().text('');
}
loading.hide();
}
function alertText(options) {
alert(options);
}
function performInstallation(page, name, guid, version) {
const developer = $('#developer', page).html().toLowerCase();
const repository_url = $('#repositoryUrl', page).html().toLowerCase();
const alertCallback = function () {
loading.show();
page.querySelector('#btnInstall').disabled = true;
ApiClient.installPlugin(name, guid, version).then(() => {
loading.hide();
alertText(globalize.translate('MessagePluginInstalled'));
}).catch(() => {
alertText(globalize.translate('MessagePluginInstallError'));
});
};
// Check the repository URL for the official Jellyfin repository domain, or
// present the warning for 3rd party plugins.
if (!repository_url.startsWith("https://repo.jellyfin.org/")) {
loading.hide();
let msg = globalize.translate('MessagePluginInstallDisclaimer');
msg += '<br/>';
msg += '<br/>';
msg += globalize.translate('PleaseConfirmPluginInstallation');
confirm(msg, globalize.translate('HeaderConfirmPluginInstallation')).then(function () {
alertCallback();
}).catch(() => {
console.debug('plugin not installed');
});
} else {
alertCallback();
}
}
export default function(view, params) {
$('.addPluginForm', view).on('submit', function () {
loading.show();
const page = $(this).parents('#addPluginPage')[0];
const name = params.name;
const guid = params.guid;
ApiClient.getInstalledPlugins().then(function (plugins) {
const installedPlugin = plugins.filter(function (plugin) {
return plugin.Name == name;
})[0];
const version = $('#selectVersion', page).val();
if (installedPlugin && installedPlugin.Version === version) {
loading.hide();
Dashboard.alert({
message: globalize.translate('MessageAlreadyInstalled'),
title: globalize.translate('HeaderPluginInstallation')
});
} else {
performInstallation(page, name, guid, version);
}
}).catch(() => {
alertText(globalize.translate('MessageGetInstalledPluginsError'));
});
return false;
});
view.addEventListener('viewshow', function () {
const page = this;
loading.show();
const name = params.name;
const guid = params.guid;
const promise1 = ApiClient.getPackageInfo(name, guid);
const promise2 = ApiClient.getInstalledPlugins();
Promise.all([promise1, promise2]).then(function (responses) {
renderPackage(responses[0], responses[1], page);
});
});
}