Properly check for different author and show it as a warning when the author has changed. Properly show signature warnings when installing a script.

This commit is contained in:
Koen 2023-09-28 16:44:40 +02:00
parent f19b7fa584
commit 1531a558a5
5 changed files with 80 additions and 34 deletions

View file

@ -42,7 +42,8 @@ class AddSourceActivity : AppCompatActivity() {
private val _client = ManagedHttpClient();
private var _config : SourcePluginConfig? = null;
private var _config: SourcePluginConfig? = null;
private var _script: String? = null;
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState);
@ -81,7 +82,7 @@ class AddSourceActivity : AppCompatActivity() {
}
_buttonInstall.setOnClickListener {
_config?.let {
install(_config!!);
install(_config!!, _script!!);
};
};
@ -114,6 +115,7 @@ class AddSourceActivity : AppCompatActivity() {
setLoading(true);
lifecycleScope.launch(Dispatchers.IO) {
val config: SourcePluginConfig;
try {
val configResp = _client.get(url);
if(!configResp.isOk)
@ -121,33 +123,51 @@ class AddSourceActivity : AppCompatActivity() {
val configJson = configResp.body?.string();
if(configJson.isNullOrEmpty())
throw IllegalStateException("No response");
val config = SourcePluginConfig.fromJson(configJson, url);
withContext(Dispatchers.Main) {
loadConfig(config);
}
}
catch(ex: SerializationException) {
config = SourcePluginConfig.fromJson(configJson, url);
} catch(ex: SerializationException) {
Logger.e(TAG, "Failed decode config", ex);
withContext(Dispatchers.Main) {
UIDialogs.showDialog(this@AddSourceActivity, R.drawable.ic_error,
"Invalid Config Format", null, null,
0, UIDialogs.Action("Ok", { finish() }, UIDialogs.ActionStyle.PRIMARY));
};
}
catch(ex: Exception) {
return@launch;
} catch(ex: Exception) {
Logger.e(TAG, "Failed fetch config", ex);
withContext(Dispatchers.Main) {
UIDialogs.showGeneralErrorDialog(this@AddSourceActivity, "Failed to fetch configuration", ex);
};
return@launch;
}
val script: String?
try {
val scriptResp = _client.get(config.absoluteScriptUrl);
if (!scriptResp.isOk)
throw IllegalStateException("script not available [${scriptResp.code}]");
script = scriptResp.body?.string();
if (script.isNullOrEmpty())
throw IllegalStateException("script empty");
} catch (ex: Exception) {
Logger.e(TAG, "Failed fetch script", ex);
withContext(Dispatchers.Main) {
UIDialogs.showGeneralErrorDialog(this@AddSourceActivity, "Failed to fetch script", ex);
};
return@launch;
}
withContext(Dispatchers.Main) {
loadConfig(config, script);
}
};
}
fun loadConfig(config: SourcePluginConfig) {
private fun loadConfig(config: SourcePluginConfig, script: String) {
_config = config;
_script = script;
_sourceHeader.loadConfig(config);
_sourceHeader.loadConfig(config, script);
_sourcePermissions.removeAllViews();
_sourceWarnings.removeAllViews();
@ -171,7 +191,7 @@ class AddSourceActivity : AppCompatActivity() {
val pastelRed = resources.getColor(R.color.pastel_red);
for(warning in config.getWarnings())
for(warning in config.getWarnings(script))
_sourceWarnings.addView(
SourceInfoView(this,
R.drawable.ic_security_pred,
@ -182,8 +202,8 @@ class AddSourceActivity : AppCompatActivity() {
setLoading(false);
}
fun install(config: SourcePluginConfig) {
StatePlugins.instance.installPlugin(this, lifecycleScope, config) {
fun install(config: SourcePluginConfig, script: String) {
StatePlugins.instance.installPlugin(this, lifecycleScope, config, script) {
if(it)
backToSources();
}

View file

@ -4,6 +4,7 @@ import android.net.Uri
import com.futo.platformplayer.SignatureProvider
import com.futo.platformplayer.api.media.Serializer
import com.futo.platformplayer.engine.IV8PluginConfig
import com.futo.platformplayer.states.StatePlugins
import kotlinx.serialization.decodeFromString
import java.net.URL
import java.util.*
@ -78,6 +79,15 @@ class SourcePluginConfig(
fun getWarnings(scriptToCheck: String? = null) : List<Pair<String,String>> {
val list = mutableListOf<Pair<String,String>>();
val currentlyInstalledPlugin = StatePlugins.instance.getPlugin(id);
if (currentlyInstalledPlugin != null) {
if (currentlyInstalledPlugin.config.scriptPublicKey != scriptPublicKey) {
list.add(Pair(
"Different Author",
"This plugin was signed by a different author. Please ensure that this is correct and that the plugin was not provided by a malicious actor."));
}
}
if(scriptPublicKey.isNullOrEmpty() || scriptSignature.isNullOrEmpty())
list.add(Pair(
"Missing Signature",

View file

@ -185,7 +185,7 @@ class SourceDetailFragment : MainFragment() {
val config = _config;
if (config != null) {
_sourceHeader.loadConfig(config);
_sourceHeader.loadConfig(config, StatePlugins.instance.getScript(config.id));
} else {
_sourceHeader.clear();
}

View file

@ -177,18 +177,16 @@ class StatePlugins {
}
fun installPlugin(context: Context, scope: CoroutineScope, sourceUrl: String, handler: ((Boolean) -> Unit)? = null) {
scope.launch(Dispatchers.IO) {
val client = ManagedHttpClient();
val config: SourcePluginConfig;
try {
val configResp = ManagedHttpClient().get(sourceUrl);
val configResp = client.get(sourceUrl);
if(!configResp.isOk)
throw IllegalStateException("Failed request with ${configResp.code}");
val configJson = configResp.body?.string();
if(configJson.isNullOrEmpty())
throw IllegalStateException("No response");
val config = SourcePluginConfig.fromJson(configJson, sourceUrl);
withContext(Dispatchers.Main) {
installPlugin(context, scope, config, handler);
}
config = SourcePluginConfig.fromJson(configJson, sourceUrl);
}
catch(ex: SerializationException) {
Logger.e(TAG, "Failed decode config", ex);
@ -199,8 +197,8 @@ class StatePlugins {
finish();
handler?.invoke(false);
}, UIDialogs.ActionStyle.PRIMARY));
};
return@launch;
}
catch(ex: Exception) {
Logger.e(TAG, "Failed fetch config", ex);
@ -209,13 +207,36 @@ class StatePlugins {
handler?.invoke(false);
});
};
return@launch;
}
val script: String?
try {
val scriptResp = client.get(config.absoluteScriptUrl);
if (!scriptResp.isOk)
throw IllegalStateException("script not available [${scriptResp.code}]");
script = scriptResp.body?.string();
if (script.isNullOrEmpty())
throw IllegalStateException("script empty");
} catch (ex: Exception) {
Logger.e(TAG, "Failed fetch script", ex);
withContext(Dispatchers.Main) {
UIDialogs.showGeneralErrorDialog(context, "Failed to fetch script", ex);
};
return@launch;
}
withContext(Dispatchers.Main) {
installPlugin(context, scope, config, script, handler);
}
}
}
fun installPlugin(context: Context, scope: CoroutineScope, config: SourcePluginConfig, handler: ((Boolean)->Unit)? = null) {
fun installPlugin(context: Context, scope: CoroutineScope, config: SourcePluginConfig, script: String, handler: ((Boolean)->Unit)? = null) {
val client = ManagedHttpClient();
val warnings = config.getWarnings();
if (script.isEmpty())
throw IllegalStateException("script empty");
fun doInstall(reinstall: Boolean) {
UIDialogs.showDialogProgress(context) {
@ -224,13 +245,6 @@ class StatePlugins {
scope.launch(Dispatchers.IO) {
try {
val scriptResp = client.get(config.absoluteScriptUrl);
if (!scriptResp.isOk)
throw IllegalStateException("script not available [${scriptResp.code}]");
val script = scriptResp.body?.string();
if (script.isNullOrEmpty())
throw IllegalStateException("script empty");
withContext(Dispatchers.Main) {
it.setText("Validating script...");
it.setProgress(0.25);

View file

@ -55,7 +55,7 @@ class SourceHeaderView : LinearLayout {
};
}
fun loadConfig(config: SourcePluginConfig) {
fun loadConfig(config: SourcePluginConfig, script: String?) {
_config = config;
val loadedIcon = StatePlugins.instance.getPluginIconOrNull(config.id);
@ -80,8 +80,10 @@ class SourceHeaderView : LinearLayout {
_sourceBy.setTextColor(Color.WHITE);
if (!config.scriptPublicKey.isNullOrEmpty() && !config.scriptSignature.isNullOrEmpty()) {
val script = StatePlugins.instance.getScript(config.id);
if (script != null && config.validate(script)) {
if (script == null) {
_sourceSignature.setTextColor(Color.rgb(0xAC, 0xAC, 0xAC));
_sourceSignature.text = "Script is not available";
} else if (config.validate(script)) {
_sourceSignature.setTextColor(Color.rgb(0, 255, 0));
_sourceSignature.text = "Signature is valid";
} else {