From 509cbabedb9428614e85637e9a51897a98d0010d Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Mon, 29 May 2023 10:57:17 -0400 Subject: [PATCH 1/9] 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. --- .../dashboard/plugins/add/index.html | 4 +- .../dashboard/plugins/add/index.js | 12 +++++- .../dashboard/plugins/repositories/index.js | 37 +++++++++++++++---- src/strings/en-us.json | 5 ++- 4 files changed, 48 insertions(+), 10 deletions(-) diff --git a/src/controllers/dashboard/plugins/add/index.html b/src/controllers/dashboard/plugins/add/index.html index 81c671d5bb..d0fbedc063 100644 --- a/src/controllers/dashboard/plugins/add/index.html +++ b/src/controllers/dashboard/plugins/add/index.html @@ -34,7 +34,9 @@
-

+

Developer:

+

Repository Name:

+

Repository URL:

diff --git a/src/controllers/dashboard/plugins/add/index.js b/src/controllers/dashboard/plugins/add/index.js index 889879abf8..772a421f7d 100644 --- a/src/controllers/dashboard/plugins/add/index.js +++ b/src/controllers/dashboard/plugins/add/index.js @@ -50,6 +50,8 @@ function renderPackage(pkg, installedPlugins, page) { return ip.Name == pkg.name; })[0]; + console.log(pkg) + populateVersions(pkg, page, installedPlugin); populateHistory(pkg, page); @@ -65,6 +67,11 @@ function renderPackage(pkg, installedPlugins, page) { $('#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', '' + installedPlugin.Version + ''); @@ -82,6 +89,7 @@ function alertText(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(); @@ -94,7 +102,9 @@ function performInstallation(page, name, guid, version) { }); }; - if (developer !== 'jellyfin') { + // 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 += '
'; diff --git a/src/controllers/dashboard/plugins/repositories/index.js b/src/controllers/dashboard/plugins/repositories/index.js index eff2de40c4..b208299b97 100644 --- a/src/controllers/dashboard/plugins/repositories/index.js +++ b/src/controllers/dashboard/plugins/repositories/index.js @@ -2,6 +2,7 @@ import loading from '../../../../components/loading/loading'; import libraryMenu from '../../../../scripts/libraryMenu'; import globalize from '../../../../scripts/globalize'; import dialogHelper from '../../../../components/dialogHelper/dialogHelper'; +import confirm from '../../../../components/confirm/confirm'; import '../../../../elements/emby-button/emby-button'; import '../../../../elements/emby-checkbox/emby-checkbox'; @@ -166,14 +167,36 @@ export default function(view) { dialog.querySelector('.newPluginForm').addEventListener('submit', e => { e.preventDefault(); - repositories.push({ - Name: dialog.querySelector('#txtRepositoryName').value, - Url: dialog.querySelector('#txtRepositoryUrl').value, - Enabled: true - }); + const repository_url = dialog.querySelector('#txtRepositoryUrl').value.toLowerCase(); + + const alertCallback = function () { + repositories.push({ + Name: dialog.querySelector('#txtRepositoryName').value, + Url: dialog.querySelector('#txtRepositoryUrl').value, + Enabled: true + }); + saveList(view); + dialogHelper.close(dialog); + }; + + // 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/")) { + let msg = globalize.translate('MessageRepositoryInstallDisclaimer'); + msg += '
'; + msg += '
'; + msg += globalize.translate('PleaseConfirmRepositoryInstallation'); + + confirm(msg, globalize.translate('HeaderConfirmRepositoryInstallation')).then(function () { + alertCallback(); + }).catch(() => { + console.debug('repository not installed'); + dialogHelper.close(dialog); + }); + } else { + alertCallback(); + } - saveList(view); - dialogHelper.close(dialog); return false; }); diff --git a/src/strings/en-us.json b/src/strings/en-us.json index 1f1d158a17..5e682c3c60 100644 --- a/src/strings/en-us.json +++ b/src/strings/en-us.json @@ -333,6 +333,7 @@ "HeaderCodecProfileHelp": "Codec profiles indicate the limitations of a device when playing specific codecs. If a limitation applies then the media will be transcoded, even if the codec is configured for direct playback.", "HeaderConfigureRemoteAccess": "Set up Remote Access", "HeaderConfirmPluginInstallation": "Confirm Plugin Installation", + "HeaderConfirmRepositoryInstallation": "Confirm Plugin Repository Installation", "HeaderConfirmProfileDeletion": "Confirm Profile Deletion", "HeaderConfirmRevokeApiKey": "Revoke API Key", "HeaderConnectionFailure": "Connection Failure", @@ -1078,10 +1079,11 @@ "MessagePleaseEnsureInternetMetadata": "Please ensure downloading of internet metadata is enabled.", "MessagePleaseWait": "Please wait. This may take a minute.", "MessagePluginConfigurationRequiresLocalAccess": "To set up this plugin please sign in to your local server directly.", - "MessagePluginInstallDisclaimer": "Plugins built by community members are a great way to enhance your experience with additional features and benefits. Before installing, please be aware of the effects they may have on your server, such as longer library scans, additional background processing, and decreased system stability.", + "MessagePluginInstallDisclaimer": "WARNING: Installing a third party plugin carries risks. It may contain unstable or malicious code, and may change at any time. Only install plugins from authors that you trust, and please be aware of the potential effects it may have, including external service queries, longer library scans, or additional background processing.", "MessagePluginInstalled": "The plugin has been successfully installed. The server will need to be restarted for changes to take effect.", "MessagePluginInstallError": "An error occurred while installing the plugin.", "MessageReenableUser": "See below to reenable", + "MessageRepositoryInstallDisclaimer": "WARNING: Installing a third party plugin repository carries risks. It may contain unstable or malicious code, and may change at any time. Only install repositories from authors that you trust.", "MessageSent": "Message sent.", "MessageSyncPlayCreateGroupDenied": "Permission required to create a group.", "MessageSyncPlayDisabled": "SyncPlay disabled.", @@ -1296,6 +1298,7 @@ "PlayNextEpisodeAutomatically": "Play next episode automatically", "PleaseAddAtLeastOneFolder": "Please add at least one folder to this library by clicking the '+' button in 'Folders' section.", "PleaseConfirmPluginInstallation": "Please click OK to confirm you've read the above and wish to proceed with the plugin installation.", + "PleaseConfirmRepositoryInstallation": "Please click OK to confirm you've read the above and wish to proceed with the plugin repository installation.", "PleaseEnterNameOrId": "Please enter a name or an external ID.", "PleaseRestartServerName": "Please restart Jellyfin on {0}.", "PleaseSelectTwoItems": "Please select at least two items.", From da9eece6c06c8238e338d47a2c3eb4e89e13b404 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Mon, 29 May 2023 11:19:13 -0400 Subject: [PATCH 2/9] Remove extra console.log --- src/controllers/dashboard/plugins/add/index.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/controllers/dashboard/plugins/add/index.js b/src/controllers/dashboard/plugins/add/index.js index 772a421f7d..cdfba0140e 100644 --- a/src/controllers/dashboard/plugins/add/index.js +++ b/src/controllers/dashboard/plugins/add/index.js @@ -50,8 +50,6 @@ function renderPackage(pkg, installedPlugins, page) { return ip.Name == pkg.name; })[0]; - console.log(pkg) - populateVersions(pkg, page, installedPlugin); populateHistory(pkg, page); From a11d74ae68c8b9317595d39750f14ed56f509eda Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Tue, 30 May 2023 09:21:41 -0400 Subject: [PATCH 3/9] Use camelCase variable name --- src/controllers/dashboard/plugins/add/index.js | 4 ++-- src/controllers/dashboard/plugins/repositories/index.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/controllers/dashboard/plugins/add/index.js b/src/controllers/dashboard/plugins/add/index.js index cdfba0140e..8ee3b61268 100644 --- a/src/controllers/dashboard/plugins/add/index.js +++ b/src/controllers/dashboard/plugins/add/index.js @@ -87,7 +87,7 @@ function alertText(options) { function performInstallation(page, name, guid, version) { const developer = $('#developer', page).html().toLowerCase(); - const repository_url = $('#repositoryUrl', page).html().toLowerCase(); + const repositoryUrl = $('#repositoryUrl', page).html().toLowerCase(); const alertCallback = function () { loading.show(); @@ -102,7 +102,7 @@ function performInstallation(page, name, guid, version) { // 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/")) { + if (!repositoryUrl.startsWith("https://repo.jellyfin.org/")) { loading.hide(); let msg = globalize.translate('MessagePluginInstallDisclaimer'); msg += '
'; diff --git a/src/controllers/dashboard/plugins/repositories/index.js b/src/controllers/dashboard/plugins/repositories/index.js index b208299b97..d8d748b3b8 100644 --- a/src/controllers/dashboard/plugins/repositories/index.js +++ b/src/controllers/dashboard/plugins/repositories/index.js @@ -167,7 +167,7 @@ export default function(view) { dialog.querySelector('.newPluginForm').addEventListener('submit', e => { e.preventDefault(); - const repository_url = dialog.querySelector('#txtRepositoryUrl').value.toLowerCase(); + const repositoryUrl = dialog.querySelector('#txtRepositoryUrl').value.toLowerCase(); const alertCallback = function () { repositories.push({ @@ -181,7 +181,7 @@ export default function(view) { // 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/")) { + if (!repositoryUrl.startsWith("https://repo.jellyfin.org/")) { let msg = globalize.translate('MessageRepositoryInstallDisclaimer'); msg += '
'; msg += '
'; From dd004ec06b7161622a282190dffe9063690d76fe Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Tue, 30 May 2023 09:25:32 -0400 Subject: [PATCH 4/9] Add labels to i8n --- src/controllers/dashboard/plugins/add/index.html | 6 +++--- src/strings/en-us.json | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/controllers/dashboard/plugins/add/index.html b/src/controllers/dashboard/plugins/add/index.html index d0fbedc063..8ecd89ab8b 100644 --- a/src/controllers/dashboard/plugins/add/index.html +++ b/src/controllers/dashboard/plugins/add/index.html @@ -34,9 +34,9 @@
-

Developer:

-

Repository Name:

-

Repository URL:

+

${LabelDeveloper}:

+

${LabelRepositoryName}:

+

${LabelRepositoryUrl}:

diff --git a/src/strings/en-us.json b/src/strings/en-us.json index 5e682c3c60..d507bed67e 100644 --- a/src/strings/en-us.json +++ b/src/strings/en-us.json @@ -603,6 +603,7 @@ "LabelDefaultUser": "Default user:", "LabelDefaultUserHelp": "Determine which user library should be displayed on connected devices. This can be overridden for each device using profiles.", "LabelDeinterlaceMethod": "Deinterlacing method:", + "LabelDeveloper": "Developer", "LabelDeviceDescription": "Device description:", "LabelDidlMode": "DIDL mode:", "LabelDisableCustomCss": "Disable custom CSS code for theming/branding provided from the server.", From cf530b30d5ca17a3e35b4e6faadf45f277b625da Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Tue, 30 May 2023 09:29:06 -0400 Subject: [PATCH 5/9] Fix linting errors * Remove superfluous variable * Remove extra random spaces from editor * Use single-quotes around text --- src/controllers/dashboard/plugins/add/index.js | 3 +-- src/controllers/dashboard/plugins/repositories/index.js | 8 ++++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/controllers/dashboard/plugins/add/index.js b/src/controllers/dashboard/plugins/add/index.js index 8ee3b61268..70071e38d1 100644 --- a/src/controllers/dashboard/plugins/add/index.js +++ b/src/controllers/dashboard/plugins/add/index.js @@ -86,7 +86,6 @@ function alertText(options) { } function performInstallation(page, name, guid, version) { - const developer = $('#developer', page).html().toLowerCase(); const repositoryUrl = $('#repositoryUrl', page).html().toLowerCase(); const alertCallback = function () { @@ -102,7 +101,7 @@ function performInstallation(page, name, guid, version) { // Check the repository URL for the official Jellyfin repository domain, or // present the warning for 3rd party plugins. - if (!repositoryUrl.startsWith("https://repo.jellyfin.org/")) { + if (!repositoryUrl.startsWith('https://repo.jellyfin.org/')) { loading.hide(); let msg = globalize.translate('MessagePluginInstallDisclaimer'); msg += '
'; diff --git a/src/controllers/dashboard/plugins/repositories/index.js b/src/controllers/dashboard/plugins/repositories/index.js index d8d748b3b8..fc8c918ed4 100644 --- a/src/controllers/dashboard/plugins/repositories/index.js +++ b/src/controllers/dashboard/plugins/repositories/index.js @@ -168,7 +168,7 @@ export default function(view) { e.preventDefault(); const repositoryUrl = dialog.querySelector('#txtRepositoryUrl').value.toLowerCase(); - + const alertCallback = function () { repositories.push({ Name: dialog.querySelector('#txtRepositoryName').value, @@ -178,15 +178,15 @@ export default function(view) { saveList(view); dialogHelper.close(dialog); }; - + // Check the repository URL for the official Jellyfin repository domain, or // present the warning for 3rd party plugins. - if (!repositoryUrl.startsWith("https://repo.jellyfin.org/")) { + if (!repositoryUrl.startsWith('https://repo.jellyfin.org/')) { let msg = globalize.translate('MessageRepositoryInstallDisclaimer'); msg += '
'; msg += '
'; msg += globalize.translate('PleaseConfirmRepositoryInstallation'); - + confirm(msg, globalize.translate('HeaderConfirmRepositoryInstallation')).then(function () { alertCallback(); }).catch(() => { From 919be18c8409949d701f77b78431c4e6a7a05627 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Tue, 30 May 2023 10:13:21 -0400 Subject: [PATCH 6/9] Fix syntax error in HTML --- src/controllers/dashboard/plugins/add/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/dashboard/plugins/add/index.html b/src/controllers/dashboard/plugins/add/index.html index 8ecd89ab8b..69edb941ca 100644 --- a/src/controllers/dashboard/plugins/add/index.html +++ b/src/controllers/dashboard/plugins/add/index.html @@ -36,7 +36,7 @@

${LabelDeveloper}:

${LabelRepositoryName}:

-

${LabelRepositoryUrl}:

+

${LabelRepositoryUrl}:

From f1b0b504ddcb269371b9b6dc27920415ad4faa50 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Tue, 30 May 2023 10:35:46 -0400 Subject: [PATCH 7/9] Check for pkg.versions.length Co-authored-by: Niels van Velzen --- src/controllers/dashboard/plugins/add/index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/controllers/dashboard/plugins/add/index.js b/src/controllers/dashboard/plugins/add/index.js index 70071e38d1..8bca658a37 100644 --- a/src/controllers/dashboard/plugins/add/index.js +++ b/src/controllers/dashboard/plugins/add/index.js @@ -68,8 +68,10 @@ function renderPackage(pkg, installedPlugins, page) { // 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. +if (pkg.versions.length) { $('#repositoryName', page).text(pkg.versions[0].repositoryName); $('#repositoryUrl', page).text(pkg.versions[0].repositoryUrl); + } if (installedPlugin) { const currentVersionText = globalize.translate('MessageYouHaveVersionInstalled', '' + installedPlugin.Version + ''); From 93d63330fd4e8555733c930ebb5b51c7d256a494 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Tue, 30 May 2023 10:39:27 -0400 Subject: [PATCH 8/9] Fix conditional formatting and add fallback --- src/controllers/dashboard/plugins/add/index.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/controllers/dashboard/plugins/add/index.js b/src/controllers/dashboard/plugins/add/index.js index 8bca658a37..0792494f79 100644 --- a/src/controllers/dashboard/plugins/add/index.js +++ b/src/controllers/dashboard/plugins/add/index.js @@ -67,11 +67,14 @@ function renderPackage(pkg, installedPlugins, page) { $('#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. -if (pkg.versions.length) { - $('#repositoryName', page).text(pkg.versions[0].repositoryName); - $('#repositoryUrl', page).text(pkg.versions[0].repositoryUrl); - } + // version to get this information. If it's missing (no versions), then say so. + if (pkg.versions.length) { + $('#repositoryName', page).text(pkg.versions[0].repositoryName); + $('#repositoryUrl', page).text(pkg.versions[0].repositoryUrl); + } else { + $('#repositoryName', page).text('Unknown (no versions)'); + $('#repositoryUrl', page).text('Unknown (no versions)'); + } if (installedPlugin) { const currentVersionText = globalize.translate('MessageYouHaveVersionInstalled', '' + installedPlugin.Version + ''); From eaae0f3c557309ce625d30b478fb700a954fe80d Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Wed, 28 Jun 2023 22:49:58 -0400 Subject: [PATCH 9/9] Add translation of "unknown" repo details --- src/controllers/dashboard/plugins/add/index.js | 4 ++-- src/strings/en-us.json | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/controllers/dashboard/plugins/add/index.js b/src/controllers/dashboard/plugins/add/index.js index 0792494f79..558d548d0c 100644 --- a/src/controllers/dashboard/plugins/add/index.js +++ b/src/controllers/dashboard/plugins/add/index.js @@ -72,8 +72,8 @@ function renderPackage(pkg, installedPlugins, page) { $('#repositoryName', page).text(pkg.versions[0].repositoryName); $('#repositoryUrl', page).text(pkg.versions[0].repositoryUrl); } else { - $('#repositoryName', page).text('Unknown (no versions)'); - $('#repositoryUrl', page).text('Unknown (no versions)'); + $('#repositoryName', page).text(globalize.translate('Unknown')); + $('#repositoryUrl', page).text(globalize.translate('Unknown')); } if (installedPlugin) { diff --git a/src/strings/en-us.json b/src/strings/en-us.json index d507bed67e..3be00ce00d 100644 --- a/src/strings/en-us.json +++ b/src/strings/en-us.json @@ -1669,5 +1669,6 @@ "MediaInfoBlPresentFlag": "DV bl preset flag", "MediaInfoDvBlSignalCompatibilityId": "DV bl signal compatibility id", "LabelTonemappingMode": "Tone mapping mode:", - "TonemappingModeHelp": "Select the tone mapping mode. If you experience blown out highlights try switching to the RGB mode." + "TonemappingModeHelp": "Select the tone mapping mode. If you experience blown out highlights try switching to the RGB mode.", + "Unknown": "Unknown" }