mirror of
https://github.com/BrenBroZAYT/gameyfin.git
synced 2026-06-15 16:20:03 +00:00
Minor refactoring
Added UNTRUSTED trust level for plugins with invalid signature
This commit is contained in:
@@ -81,6 +81,10 @@ export function PluginManagementCard({plugin, updatePlugin}: {
|
||||
return <Tooltip color="foreground" placement="bottom" content="3rd party plugin">
|
||||
<SealWarning/>
|
||||
</Tooltip>;
|
||||
case PluginTrustLevel.UNTRUSTED:
|
||||
return <Tooltip color="foreground" placement="bottom" content="Plugin verification failed">
|
||||
<SealWarning weight="fill" className="fill-danger"/>
|
||||
</Tooltip>;
|
||||
default:
|
||||
return <Tooltip color="foreground" placement="bottom" content="Unkown verification status">
|
||||
<SealQuestion/>
|
||||
|
||||
+38
-26
@@ -31,6 +31,7 @@ class GameyfinPluginManager(
|
||||
}
|
||||
|
||||
private val log = KotlinLogging.logger {}
|
||||
private val publicKey: PublicKey = loadPluginSignaturePublicKey()
|
||||
|
||||
// This took me way too long to figure out...
|
||||
// But I learned a lot about Kotlin and Java interoperability in the process
|
||||
@@ -66,37 +67,46 @@ class GameyfinPluginManager(
|
||||
override fun loadPluginFromPath(pluginPath: Path?): PluginWrapper? {
|
||||
val pluginWrapper = super.loadPluginFromPath(pluginPath)
|
||||
|
||||
if (pluginWrapper != null) {
|
||||
// Inject config after loading, before starting
|
||||
configurePlugin(pluginWrapper)
|
||||
if (pluginWrapper == null || pluginPath == null) return null
|
||||
|
||||
// Update or create the PluginManagementEntry
|
||||
if (pluginPath != null) {
|
||||
// Inject config after loading, before starting
|
||||
configurePlugin(pluginWrapper)
|
||||
|
||||
// Set priority to the max value of the current plugins + 1 (which is the lowest priority) or 1 if there are no entries
|
||||
val currentMaxPriority = pluginManagementRepository.findMaxPriority() ?: 0
|
||||
var pluginManagementEntry = pluginManagementRepository.findByIdOrNull(pluginWrapper.pluginId)
|
||||
|
||||
val pluginManagementEntry = pluginManagementRepository.findByIdOrNull(pluginWrapper.pluginId)
|
||||
?: PluginManagementEntry(pluginId = pluginWrapper.pluginId, priority = currentMaxPriority + 1)
|
||||
if (pluginManagementEntry == null) {
|
||||
// Create a new entry
|
||||
|
||||
if (pluginPath.extension == "jar") {
|
||||
log.debug { "Verifying plugin signature for ${pluginWrapper.pluginId}" }
|
||||
pluginManagementEntry.trustLevel = verifyPluginSignature(pluginPath)
|
||||
log.debug { "Plugin ${pluginWrapper.pluginId} verification status: ${pluginManagementEntry.trustLevel}" }
|
||||
} else {
|
||||
pluginManagementEntry.trustLevel = PluginTrustLevel.BUNDLED
|
||||
}
|
||||
// Set priority to the max value of the current plugins + 1 (which is the lowest priority) or 1 if there are no entries
|
||||
val currentMaxPriority = pluginManagementRepository.findMaxPriority() ?: 0
|
||||
|
||||
if (pluginManagementEntry.trustLevel in listOf(PluginTrustLevel.OFFICIAL, PluginTrustLevel.BUNDLED)) {
|
||||
pluginManagementEntry.enabled = true
|
||||
log.info { "Plugin ${pluginWrapper.pluginId} verified, starting" }
|
||||
startPlugin(pluginWrapper.pluginId)
|
||||
}
|
||||
pluginManagementEntry =
|
||||
PluginManagementEntry(pluginId = pluginWrapper.pluginId, priority = currentMaxPriority + 1)
|
||||
|
||||
pluginManagementRepository.save(pluginManagementEntry)
|
||||
pluginManagementEntry.trustLevel = when (pluginPath.extension) {
|
||||
"jar" -> verifyPluginSignature(pluginPath)
|
||||
else -> PluginTrustLevel.BUNDLED
|
||||
}
|
||||
|
||||
// If the plugin is official or bundled, we can enable it and start it by default
|
||||
if (pluginManagementEntry.trustLevel == PluginTrustLevel.OFFICIAL
|
||||
|| pluginManagementEntry.trustLevel == PluginTrustLevel.BUNDLED
|
||||
) {
|
||||
pluginManagementEntry.enabled = true
|
||||
log.info { "Plugin ${pluginWrapper.pluginId} verified, starting" }
|
||||
startPlugin(pluginWrapper.pluginId)
|
||||
}
|
||||
} else {
|
||||
// Just re-verify the plugin if it was already in the database
|
||||
pluginManagementEntry.trustLevel = when (pluginPath.extension) {
|
||||
"jar" -> verifyPluginSignature(pluginPath)
|
||||
else -> PluginTrustLevel.BUNDLED
|
||||
}
|
||||
}
|
||||
|
||||
log.debug { "Plugin ${pluginWrapper.pluginId} verification status: ${pluginManagementEntry.trustLevel}" }
|
||||
pluginManagementRepository.save(pluginManagementEntry)
|
||||
|
||||
return pluginWrapper
|
||||
}
|
||||
|
||||
@@ -174,13 +184,15 @@ class GameyfinPluginManager(
|
||||
return pluginConfigRepository.findAllById_PluginId(pluginId).associate { it.id.key to it.value }
|
||||
}
|
||||
|
||||
fun verifyPluginSignature(pluginPath: Path): PluginTrustLevel {
|
||||
private fun loadPluginSignaturePublicKey(): PublicKey {
|
||||
val certFactory: CertificateFactory = CertificateFactory.getInstance("X.509")
|
||||
val certFileInputStream = javaClass.classLoader.getResourceAsStream(PUBLIC_KEY_FILE)
|
||||
val cert: X509Certificate = certFactory.generateCertificate(certFileInputStream) as X509Certificate
|
||||
val publicKey: PublicKey = cert.publicKey
|
||||
certFileInputStream?.close()
|
||||
return cert.publicKey
|
||||
}
|
||||
|
||||
private fun verifyPluginSignature(pluginPath: Path): PluginTrustLevel {
|
||||
val jarFile = JarFile(pluginPath.toFile(), true)
|
||||
val entries = jarFile.entries()
|
||||
|
||||
@@ -197,7 +209,7 @@ class GameyfinPluginManager(
|
||||
}
|
||||
} catch (_: SecurityException) {
|
||||
// Signature verification failed
|
||||
return PluginTrustLevel.THIRD_PARTY
|
||||
return PluginTrustLevel.UNTRUSTED
|
||||
}
|
||||
|
||||
val codeSigners = entry.codeSigners
|
||||
@@ -216,7 +228,7 @@ class GameyfinPluginManager(
|
||||
cert.verify(publicKey)
|
||||
} catch (_: Exception) {
|
||||
// Signature verification failed
|
||||
return PluginTrustLevel.THIRD_PARTY
|
||||
return PluginTrustLevel.UNTRUSTED
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -4,5 +4,6 @@ enum class PluginTrustLevel {
|
||||
BUNDLED,
|
||||
OFFICIAL,
|
||||
THIRD_PARTY,
|
||||
UNTRUSTED,
|
||||
UNKNOWN,
|
||||
}
|
||||
@@ -274,7 +274,7 @@ function statsExtracterPlugin(): PluginOption {
|
||||
const frontendFiles: Record<string, string> = {};
|
||||
frontendFiles['index.html'] = createHash('sha256').update(customIndexData.replace(/\r\n/g, '\n'), 'utf8').digest('hex');
|
||||
|
||||
const projectFileExtensions = ['.js', '.js.map', '.ts', '.ts.map', '.tsx', '.tsx.map', '.css', '.css.map'];
|
||||
const projectFileExtensions = ['.js', '.js.map', '.ts', '.ts.map', '.tsx', '.tsx.map', '.css', '.css.map', '.'];
|
||||
|
||||
const isThemeComponentsResource = (id: string) =>
|
||||
id.startsWith(themeOptions.frontendGeneratedFolder.replace(/\\/g, '/'))
|
||||
|
||||
Reference in New Issue
Block a user