mirror of
https://github.com/rhunk/SnapEnhance.git
synced 2025-06-13 05:37:48 +02:00
feat(scripting): permissions
- Java Interfaces binding
This commit is contained in:
@ -4,11 +4,13 @@ import android.os.Handler
|
|||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import me.rhunk.snapenhance.common.scripting.bindings.AbstractBinding
|
import me.rhunk.snapenhance.common.scripting.bindings.AbstractBinding
|
||||||
import me.rhunk.snapenhance.common.scripting.bindings.BindingsContext
|
import me.rhunk.snapenhance.common.scripting.bindings.BindingsContext
|
||||||
|
import me.rhunk.snapenhance.common.scripting.impl.JavaInterfaces
|
||||||
import me.rhunk.snapenhance.common.scripting.ktx.contextScope
|
import me.rhunk.snapenhance.common.scripting.ktx.contextScope
|
||||||
import me.rhunk.snapenhance.common.scripting.ktx.putFunction
|
import me.rhunk.snapenhance.common.scripting.ktx.putFunction
|
||||||
import me.rhunk.snapenhance.common.scripting.ktx.scriptable
|
import me.rhunk.snapenhance.common.scripting.ktx.scriptable
|
||||||
import me.rhunk.snapenhance.common.scripting.ktx.scriptableObject
|
import me.rhunk.snapenhance.common.scripting.ktx.scriptableObject
|
||||||
import me.rhunk.snapenhance.common.scripting.type.ModuleInfo
|
import me.rhunk.snapenhance.common.scripting.type.ModuleInfo
|
||||||
|
import me.rhunk.snapenhance.common.scripting.type.Permissions
|
||||||
import org.mozilla.javascript.Function
|
import org.mozilla.javascript.Function
|
||||||
import org.mozilla.javascript.NativeJavaObject
|
import org.mozilla.javascript.NativeJavaObject
|
||||||
import org.mozilla.javascript.ScriptableObject
|
import org.mozilla.javascript.ScriptableObject
|
||||||
@ -49,6 +51,10 @@ class JSModule(
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
registerBindings(
|
||||||
|
JavaInterfaces(),
|
||||||
|
)
|
||||||
|
|
||||||
moduleObject.putFunction("setField") { args ->
|
moduleObject.putFunction("setField") { args ->
|
||||||
val obj = args?.get(0) as? NativeJavaObject ?: return@putFunction Undefined.instance
|
val obj = args?.get(0) as? NativeJavaObject ?: return@putFunction Undefined.instance
|
||||||
val name = args[1].toString()
|
val name = args[1].toString()
|
||||||
@ -74,8 +80,12 @@ class JSModule(
|
|||||||
|
|
||||||
moduleObject.putFunction("findClass") {
|
moduleObject.putFunction("findClass") {
|
||||||
val className = it?.get(0).toString()
|
val className = it?.get(0).toString()
|
||||||
|
val useModClassLoader = it?.getOrNull(1) as? Boolean ?: false
|
||||||
|
if (useModClassLoader) moduleInfo.ensurePermissionGranted(Permissions.UNSAFE_CLASSLOADER)
|
||||||
|
|
||||||
runCatching {
|
runCatching {
|
||||||
classLoader.loadClass(className)
|
if (useModClassLoader) this::class.java.classLoader?.loadClass(className)
|
||||||
|
else classLoader.loadClass(className)
|
||||||
}.onFailure { throwable ->
|
}.onFailure { throwable ->
|
||||||
scriptRuntime.logger.error("Failed to load class $className", throwable)
|
scriptRuntime.logger.error("Failed to load class $className", throwable)
|
||||||
}.getOrNull()
|
}.getOrNull()
|
||||||
@ -83,7 +93,12 @@ class JSModule(
|
|||||||
|
|
||||||
moduleObject.putFunction("type") { args ->
|
moduleObject.putFunction("type") { args ->
|
||||||
val className = args?.get(0).toString()
|
val className = args?.get(0).toString()
|
||||||
val clazz = runCatching { classLoader.loadClass(className) }.getOrNull() ?: return@putFunction Undefined.instance
|
val useModClassLoader = args?.getOrNull(1) as? Boolean ?: false
|
||||||
|
if (useModClassLoader) moduleInfo.ensurePermissionGranted(Permissions.UNSAFE_CLASSLOADER)
|
||||||
|
|
||||||
|
val clazz = runCatching {
|
||||||
|
if (useModClassLoader) this::class.java.classLoader?.loadClass(className) else classLoader.loadClass(className)
|
||||||
|
}.getOrNull() ?: return@putFunction Undefined.instance
|
||||||
|
|
||||||
scriptableObject("JavaClassWrapper") {
|
scriptableObject("JavaClassWrapper") {
|
||||||
putFunction("newInstance") newInstance@{ args ->
|
putFunction("newInstance") newInstance@{ args ->
|
||||||
@ -116,7 +131,7 @@ class JSModule(
|
|||||||
}
|
}
|
||||||
|
|
||||||
moduleObject.putFunction("logError") { args ->
|
moduleObject.putFunction("logError") { args ->
|
||||||
scriptRuntime.logger.error(argsToString(arrayOf(args?.get(0))), args?.get(1) as? Throwable ?: Throwable())
|
scriptRuntime.logger.error(argsToString(arrayOf(args?.get(0))), args?.getOrNull(1) as? Throwable ?: Throwable())
|
||||||
Undefined.instance
|
Undefined.instance
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,8 +194,8 @@ class JSModule(
|
|||||||
contextScope {
|
contextScope {
|
||||||
name.split(".").also { split ->
|
name.split(".").also { split ->
|
||||||
val function = split.dropLast(1).fold(moduleObject) { obj, key ->
|
val function = split.dropLast(1).fold(moduleObject) { obj, key ->
|
||||||
obj.get(key, obj) as? ScriptableObject ?: return@contextScope
|
obj.get(key, obj) as? ScriptableObject ?: return@contextScope Unit
|
||||||
}.get(split.last(), moduleObject) as? Function ?: return@contextScope
|
}.get(split.last(), moduleObject) as? Function ?: return@contextScope Unit
|
||||||
|
|
||||||
runCatching {
|
runCatching {
|
||||||
function.call(this, moduleObject, moduleObject, args)
|
function.call(this, moduleObject, moduleObject, args)
|
||||||
|
@ -0,0 +1,45 @@
|
|||||||
|
package me.rhunk.snapenhance.common.scripting.impl
|
||||||
|
|
||||||
|
import me.rhunk.snapenhance.common.scripting.bindings.AbstractBinding
|
||||||
|
import me.rhunk.snapenhance.common.scripting.bindings.BindingSide
|
||||||
|
import me.rhunk.snapenhance.common.scripting.ktx.contextScope
|
||||||
|
import me.rhunk.snapenhance.common.scripting.ktx.putFunction
|
||||||
|
import me.rhunk.snapenhance.common.scripting.ktx.scriptableObject
|
||||||
|
import java.lang.reflect.Proxy
|
||||||
|
|
||||||
|
class JavaInterfaces : AbstractBinding("java-interfaces", BindingSide.COMMON) {
|
||||||
|
override fun getObject() = scriptableObject {
|
||||||
|
putFunction("runnable") {
|
||||||
|
val function = it?.get(0) as? org.mozilla.javascript.Function ?: return@putFunction null
|
||||||
|
Runnable {
|
||||||
|
contextScope {
|
||||||
|
function.call(
|
||||||
|
this,
|
||||||
|
this@scriptableObject,
|
||||||
|
this@scriptableObject,
|
||||||
|
emptyArray()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
putFunction("newProxy") { arguments ->
|
||||||
|
val javaInterface = arguments?.get(0) as? Class<*> ?: return@putFunction null
|
||||||
|
val function = arguments[1] as? org.mozilla.javascript.Function ?: return@putFunction null
|
||||||
|
|
||||||
|
Proxy.newProxyInstance(
|
||||||
|
javaInterface.classLoader,
|
||||||
|
arrayOf(javaInterface)
|
||||||
|
) { instance, method, args ->
|
||||||
|
contextScope {
|
||||||
|
function.call(
|
||||||
|
this,
|
||||||
|
this@scriptableObject,
|
||||||
|
this@scriptableObject,
|
||||||
|
arrayOf(instance, method.name, (args ?: emptyArray<Any>()).toList())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,12 +4,17 @@ import org.mozilla.javascript.Context
|
|||||||
import org.mozilla.javascript.Function
|
import org.mozilla.javascript.Function
|
||||||
import org.mozilla.javascript.Scriptable
|
import org.mozilla.javascript.Scriptable
|
||||||
import org.mozilla.javascript.ScriptableObject
|
import org.mozilla.javascript.ScriptableObject
|
||||||
|
import org.mozilla.javascript.Wrapper
|
||||||
|
|
||||||
fun contextScope(f: Context.() -> Unit) {
|
fun contextScope(f: Context.() -> Any?): Any? {
|
||||||
val context = Context.enter()
|
val context = Context.enter()
|
||||||
context.optimizationLevel = -1
|
context.optimizationLevel = -1
|
||||||
try {
|
try {
|
||||||
context.f()
|
return context.f().let {
|
||||||
|
if (it is Wrapper) {
|
||||||
|
it.unwrap()
|
||||||
|
} else it
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
Context.exit()
|
Context.exit()
|
||||||
}
|
}
|
||||||
|
@ -9,4 +9,20 @@ data class ModuleInfo(
|
|||||||
val minSnapchatVersion: Long? = null,
|
val minSnapchatVersion: Long? = null,
|
||||||
val minSEVersion: Long? = null,
|
val minSEVersion: Long? = null,
|
||||||
val grantedPermissions: List<String>,
|
val grantedPermissions: List<String>,
|
||||||
)
|
) {
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (other !is ModuleInfo) return false
|
||||||
|
if (other === this) return true
|
||||||
|
return name == other.name &&
|
||||||
|
version == other.version &&
|
||||||
|
displayName == other.displayName &&
|
||||||
|
description == other.description &&
|
||||||
|
author == other.author
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ensurePermissionGranted(permission: Permissions) {
|
||||||
|
if (!grantedPermissions.contains(permission.key)) {
|
||||||
|
throw AssertionError("Permission $permission is not granted")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package me.rhunk.snapenhance.common.scripting.type
|
||||||
|
|
||||||
|
enum class Permissions(
|
||||||
|
val key: String,
|
||||||
|
) {
|
||||||
|
UNSAFE_CLASSLOADER("unsafe-classloader"),
|
||||||
|
}
|
Reference in New Issue
Block a user