From ea90e1bdcf69adc09195503c1150908158932fc9 Mon Sep 17 00:00:00 2001 From: Koshak_Mine <52908036+KoshakMineDEV@users.noreply.github.com> Date: Sat, 4 Jan 2025 15:52:17 +0300 Subject: [PATCH 01/13] feat: introduce base for security --- .../java/com/instancify/scriptify/api/script/Script.java | 3 +++ .../api/script/security/ScriptSecurityManager.java | 8 ++++++++ .../java/com/instancify/scriptify/script/JsScript.java | 7 +++++++ .../java/com/instancify/scriptify/script/JsScript.java | 7 +++++++ 4 files changed, 25 insertions(+) create mode 100644 api/src/main/java/com/instancify/scriptify/api/script/security/ScriptSecurityManager.java diff --git a/api/src/main/java/com/instancify/scriptify/api/script/Script.java b/api/src/main/java/com/instancify/scriptify/api/script/Script.java index 0240a0b..2537337 100644 --- a/api/src/main/java/com/instancify/scriptify/api/script/Script.java +++ b/api/src/main/java/com/instancify/scriptify/api/script/Script.java @@ -4,6 +4,7 @@ import com.instancify.scriptify.api.exception.ScriptFunctionException; import com.instancify.scriptify.api.script.constant.ScriptConstantManager; import com.instancify.scriptify.api.script.function.ScriptFunctionManager; +import com.instancify.scriptify.api.script.security.ScriptSecurityManager; /** * Defines the structure of a script that can be executed. @@ -44,6 +45,8 @@ public interface Script { */ void setConstantManager(ScriptConstantManager constantManager); + ScriptSecurityManager getSecurityManager(); + /** * Evaluates and executes this script. * diff --git a/api/src/main/java/com/instancify/scriptify/api/script/security/ScriptSecurityManager.java b/api/src/main/java/com/instancify/scriptify/api/script/security/ScriptSecurityManager.java new file mode 100644 index 0000000..34d8ec5 --- /dev/null +++ b/api/src/main/java/com/instancify/scriptify/api/script/security/ScriptSecurityManager.java @@ -0,0 +1,8 @@ +package com.instancify.scriptify.api.script.security; + +public interface ScriptSecurityManager { + + boolean getSecurityMode(); + + void setSecurityMode(boolean securityMode); +} diff --git a/script-js-graalvm/src/main/java/com/instancify/scriptify/script/JsScript.java b/script-js-graalvm/src/main/java/com/instancify/scriptify/script/JsScript.java index 8801278..e1266b8 100644 --- a/script-js-graalvm/src/main/java/com/instancify/scriptify/script/JsScript.java +++ b/script-js-graalvm/src/main/java/com/instancify/scriptify/script/JsScript.java @@ -6,6 +6,7 @@ import com.instancify.scriptify.api.script.constant.ScriptConstantManager; import com.instancify.scriptify.api.script.function.ScriptFunction; import com.instancify.scriptify.api.script.function.ScriptFunctionManager; +import com.instancify.scriptify.api.script.security.ScriptSecurityManager; import org.graalvm.polyglot.*; public class JsScript implements Script { @@ -13,6 +14,7 @@ public class JsScript implements Script { private final Context context = Context.create(); private ScriptFunctionManager functionManager; private ScriptConstantManager constantManager; + private ScriptSecurityManager securityManager; @Override public ScriptFunctionManager getFunctionManager() { @@ -34,6 +36,11 @@ public void setConstantManager(ScriptConstantManager constantManager) { this.constantManager = constantManager; } + @Override + public ScriptSecurityManager getSecurityManager() { + return securityManager; + } + @Override public Value eval(String script) throws ScriptException { Value bindings = context.getBindings("js"); diff --git a/script-js-rhino/src/main/java/com/instancify/scriptify/script/JsScript.java b/script-js-rhino/src/main/java/com/instancify/scriptify/script/JsScript.java index 5175383..3e64c11 100644 --- a/script-js-rhino/src/main/java/com/instancify/scriptify/script/JsScript.java +++ b/script-js-rhino/src/main/java/com/instancify/scriptify/script/JsScript.java @@ -6,6 +6,7 @@ import com.instancify.scriptify.api.script.constant.ScriptConstantManager; import com.instancify.scriptify.api.script.function.ScriptFunction; import com.instancify.scriptify.api.script.function.ScriptFunctionManager; +import com.instancify.scriptify.api.script.security.ScriptSecurityManager; import org.mozilla.javascript.Context; import org.mozilla.javascript.ScriptableObject; @@ -15,6 +16,7 @@ public class JsScript implements Script { private final ScriptableObject scope = context.initStandardObjects(); private ScriptFunctionManager functionManager; private ScriptConstantManager constantManager; + private ScriptSecurityManager securityManager; @Override public ScriptFunctionManager getFunctionManager() { @@ -36,6 +38,11 @@ public void setConstantManager(ScriptConstantManager constantManager) { this.constantManager = constantManager; } + @Override + public ScriptSecurityManager getSecurityManager() { + return securityManager; + } + @Override public Object eval(String script) throws ScriptException { if (functionManager != null) { From 942c9a6d95ad0ceb8414e86c9fc9e787f6ae6e45 Mon Sep 17 00:00:00 2001 From: Koshak_Mine <52908036+KoshakMineDEV@users.noreply.github.com> Date: Sat, 4 Jan 2025 15:56:16 +0300 Subject: [PATCH 02/13] chore: javadoc --- .../com/instancify/scriptify/api/script/Script.java | 6 ++++++ .../api/script/security/ScriptSecurityManager.java | 13 +++++++++++++ 2 files changed, 19 insertions(+) diff --git a/api/src/main/java/com/instancify/scriptify/api/script/Script.java b/api/src/main/java/com/instancify/scriptify/api/script/Script.java index 2537337..fb38b95 100644 --- a/api/src/main/java/com/instancify/scriptify/api/script/Script.java +++ b/api/src/main/java/com/instancify/scriptify/api/script/Script.java @@ -45,6 +45,12 @@ public interface Script { */ void setConstantManager(ScriptConstantManager constantManager); + /** + * Retrieves the security manager associated with this script. + * + * @return The ScriptSecurityManager for this script + * @see ScriptSecurityManager + */ ScriptSecurityManager getSecurityManager(); /** diff --git a/api/src/main/java/com/instancify/scriptify/api/script/security/ScriptSecurityManager.java b/api/src/main/java/com/instancify/scriptify/api/script/security/ScriptSecurityManager.java index 34d8ec5..d885567 100644 --- a/api/src/main/java/com/instancify/scriptify/api/script/security/ScriptSecurityManager.java +++ b/api/src/main/java/com/instancify/scriptify/api/script/security/ScriptSecurityManager.java @@ -1,8 +1,21 @@ package com.instancify.scriptify.api.script.security; +import com.instancify.scriptify.api.script.constant.ScriptConstantManager; + public interface ScriptSecurityManager { + /** + * Gets a current security mode + * + * @return The boolean associated with security mode state + */ boolean getSecurityMode(); + /** + * Sets the current security mode + * + * @param securityMode The boolean associated with security mode state + * @see ScriptConstantManager + */ void setSecurityMode(boolean securityMode); } From 1d0dc69d66cab14487078574245131df7c585669 Mon Sep 17 00:00:00 2001 From: MEFRREEX Date: Sat, 4 Jan 2025 15:28:34 +0200 Subject: [PATCH 03/13] feat: implement standard security manager --- build.gradle.kts | 2 +- .../security/StandardSecurityManager.java | 18 ++++++++++++++++++ .../instancify/scriptify/script/JsScript.java | 13 +++++++------ .../instancify/scriptify/script/JsScript.java | 13 +++++++------ 4 files changed, 33 insertions(+), 13 deletions(-) create mode 100644 core/src/main/java/com/instancify/scriptify/core/script/security/StandardSecurityManager.java diff --git a/build.gradle.kts b/build.gradle.kts index 260ca2a..e7a022c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -12,7 +12,7 @@ java { allprojects { group = "com.instancify.scriptify" - version = "1.3.1-SNAPSHOT" + version = "1.3.2-SNAPSHOT" } subprojects { diff --git a/core/src/main/java/com/instancify/scriptify/core/script/security/StandardSecurityManager.java b/core/src/main/java/com/instancify/scriptify/core/script/security/StandardSecurityManager.java new file mode 100644 index 0000000..f2aac4e --- /dev/null +++ b/core/src/main/java/com/instancify/scriptify/core/script/security/StandardSecurityManager.java @@ -0,0 +1,18 @@ +package com.instancify.scriptify.core.script.security; + +import com.instancify.scriptify.api.script.security.ScriptSecurityManager; + +public class StandardSecurityManager implements ScriptSecurityManager { + + private boolean securityMode; + + @Override + public boolean getSecurityMode() { + return securityMode; + } + + @Override + public void setSecurityMode(boolean securityMode) { + this.securityMode = securityMode; + } +} diff --git a/script-js-graalvm/src/main/java/com/instancify/scriptify/script/JsScript.java b/script-js-graalvm/src/main/java/com/instancify/scriptify/script/JsScript.java index e1266b8..041ce37 100644 --- a/script-js-graalvm/src/main/java/com/instancify/scriptify/script/JsScript.java +++ b/script-js-graalvm/src/main/java/com/instancify/scriptify/script/JsScript.java @@ -7,14 +7,20 @@ import com.instancify.scriptify.api.script.function.ScriptFunction; import com.instancify.scriptify.api.script.function.ScriptFunctionManager; import com.instancify.scriptify.api.script.security.ScriptSecurityManager; +import com.instancify.scriptify.core.script.security.StandardSecurityManager; import org.graalvm.polyglot.*; public class JsScript implements Script { private final Context context = Context.create(); + private final ScriptSecurityManager securityManager = new StandardSecurityManager(); private ScriptFunctionManager functionManager; private ScriptConstantManager constantManager; - private ScriptSecurityManager securityManager; + + @Override + public ScriptSecurityManager getSecurityManager() { + return securityManager; + } @Override public ScriptFunctionManager getFunctionManager() { @@ -36,11 +42,6 @@ public void setConstantManager(ScriptConstantManager constantManager) { this.constantManager = constantManager; } - @Override - public ScriptSecurityManager getSecurityManager() { - return securityManager; - } - @Override public Value eval(String script) throws ScriptException { Value bindings = context.getBindings("js"); diff --git a/script-js-rhino/src/main/java/com/instancify/scriptify/script/JsScript.java b/script-js-rhino/src/main/java/com/instancify/scriptify/script/JsScript.java index 3e64c11..f1fdbe0 100644 --- a/script-js-rhino/src/main/java/com/instancify/scriptify/script/JsScript.java +++ b/script-js-rhino/src/main/java/com/instancify/scriptify/script/JsScript.java @@ -7,6 +7,7 @@ import com.instancify.scriptify.api.script.function.ScriptFunction; import com.instancify.scriptify.api.script.function.ScriptFunctionManager; import com.instancify.scriptify.api.script.security.ScriptSecurityManager; +import com.instancify.scriptify.core.script.security.StandardSecurityManager; import org.mozilla.javascript.Context; import org.mozilla.javascript.ScriptableObject; @@ -14,9 +15,14 @@ public class JsScript implements Script { private final Context context = Context.enter(); private final ScriptableObject scope = context.initStandardObjects(); + private final ScriptSecurityManager securityManager = new StandardSecurityManager(); private ScriptFunctionManager functionManager; private ScriptConstantManager constantManager; - private ScriptSecurityManager securityManager; + + @Override + public ScriptSecurityManager getSecurityManager() { + return securityManager; + } @Override public ScriptFunctionManager getFunctionManager() { @@ -38,11 +44,6 @@ public void setConstantManager(ScriptConstantManager constantManager) { this.constantManager = constantManager; } - @Override - public ScriptSecurityManager getSecurityManager() { - return securityManager; - } - @Override public Object eval(String script) throws ScriptException { if (functionManager != null) { From 29f119c4de9b323bb67c5210bbf6fc62e5d527e0 Mon Sep 17 00:00:00 2001 From: MEFRREEX Date: Sat, 4 Jan 2025 15:55:59 +0200 Subject: [PATCH 04/13] feat: start working on security excludes --- .../security/ScriptSecurityManager.java | 28 ++++++++++++- .../exclude/PackageSecurityExclude.java | 15 +++++++ .../security/exclude/PathSecurityExclude.java | 20 +++++++++ .../security/exclude/SecurityExclude.java | 42 +++++++++++++++++++ .../security/StandardSecurityManager.java | 20 +++++++++ 5 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 api/src/main/java/com/instancify/scriptify/api/script/security/exclude/PackageSecurityExclude.java create mode 100644 api/src/main/java/com/instancify/scriptify/api/script/security/exclude/PathSecurityExclude.java create mode 100644 api/src/main/java/com/instancify/scriptify/api/script/security/exclude/SecurityExclude.java diff --git a/api/src/main/java/com/instancify/scriptify/api/script/security/ScriptSecurityManager.java b/api/src/main/java/com/instancify/scriptify/api/script/security/ScriptSecurityManager.java index d885567..4790ba7 100644 --- a/api/src/main/java/com/instancify/scriptify/api/script/security/ScriptSecurityManager.java +++ b/api/src/main/java/com/instancify/scriptify/api/script/security/ScriptSecurityManager.java @@ -1,21 +1,45 @@ package com.instancify.scriptify.api.script.security; import com.instancify.scriptify.api.script.constant.ScriptConstantManager; +import com.instancify.scriptify.api.script.security.exclude.SecurityExclude; + +import java.util.Set; public interface ScriptSecurityManager { /** - * Gets a current security mode + * Gets a current security mode. * * @return The boolean associated with security mode state */ boolean getSecurityMode(); /** - * Sets the current security mode + * Sets the current security mode. * * @param securityMode The boolean associated with security mode state * @see ScriptConstantManager */ void setSecurityMode(boolean securityMode); + + /** + * Retrieves all existing exclusions for this script. + * + * @return Set with exclusions + */ + Set getExcludes(); + + /** + * Adds an exclusion for a path or package. + * + * @param exclude The exclusion to be added + */ + void addExclude(SecurityExclude exclude); + + /** + * Removes an exclusion for a path or package. + * + * @param exclude The exclusion to remove + */ + void removeExclude(SecurityExclude exclude); } diff --git a/api/src/main/java/com/instancify/scriptify/api/script/security/exclude/PackageSecurityExclude.java b/api/src/main/java/com/instancify/scriptify/api/script/security/exclude/PackageSecurityExclude.java new file mode 100644 index 0000000..d356e12 --- /dev/null +++ b/api/src/main/java/com/instancify/scriptify/api/script/security/exclude/PackageSecurityExclude.java @@ -0,0 +1,15 @@ +package com.instancify.scriptify.api.script.security.exclude; + +public class PackageSecurityExclude implements SecurityExclude { + + private final String value; + + public PackageSecurityExclude(String value) { + this.value = value; + } + + @Override + public String getValue() { + return value; + } +} diff --git a/api/src/main/java/com/instancify/scriptify/api/script/security/exclude/PathSecurityExclude.java b/api/src/main/java/com/instancify/scriptify/api/script/security/exclude/PathSecurityExclude.java new file mode 100644 index 0000000..27fe7a1 --- /dev/null +++ b/api/src/main/java/com/instancify/scriptify/api/script/security/exclude/PathSecurityExclude.java @@ -0,0 +1,20 @@ +package com.instancify.scriptify.api.script.security.exclude; + +public class PathSecurityExclude implements SecurityExclude { + + private final String value; + + public PathSecurityExclude(String value) { + this.value = value; + } + + @Override + public String getValue() { + return value; + } + + @Override + public boolean isExcluded(String value) { + return this.getValue().startsWith(value); + } +} diff --git a/api/src/main/java/com/instancify/scriptify/api/script/security/exclude/SecurityExclude.java b/api/src/main/java/com/instancify/scriptify/api/script/security/exclude/SecurityExclude.java new file mode 100644 index 0000000..2c477f4 --- /dev/null +++ b/api/src/main/java/com/instancify/scriptify/api/script/security/exclude/SecurityExclude.java @@ -0,0 +1,42 @@ +package com.instancify.scriptify.api.script.security.exclude; + +public interface SecurityExclude { + + /** + * Value to be added to the exclusion. + * + * @return The exclusion value + */ + String getValue(); + + + /** + * Checks that the value of path or packet is excluded. + * + * @param value Path or package + * @return True if excluded, otherwise false + */ + default boolean isExcluded(String value) { + return this.getValue().startsWith(value); + } + + /** + * Creates a new exclusion instance for the package. + * + * @param value A package that will be excluded + * @return A new exclusion instance for the package + */ + static PackageSecurityExclude ofPackage(String value) { + return new PackageSecurityExclude(value); + } + + /** + * Creates a new exclusion instance for the path. + * + * @param value A path that will be excluded + * @return A new exclusion instance for the path + */ + static PathSecurityExclude ofPath(String value) { + return new PathSecurityExclude(value); + } +} diff --git a/core/src/main/java/com/instancify/scriptify/core/script/security/StandardSecurityManager.java b/core/src/main/java/com/instancify/scriptify/core/script/security/StandardSecurityManager.java index f2aac4e..050378d 100644 --- a/core/src/main/java/com/instancify/scriptify/core/script/security/StandardSecurityManager.java +++ b/core/src/main/java/com/instancify/scriptify/core/script/security/StandardSecurityManager.java @@ -1,10 +1,15 @@ package com.instancify.scriptify.core.script.security; import com.instancify.scriptify.api.script.security.ScriptSecurityManager; +import com.instancify.scriptify.api.script.security.exclude.SecurityExclude; + +import java.util.HashSet; +import java.util.Set; public class StandardSecurityManager implements ScriptSecurityManager { private boolean securityMode; + private final Set excludes = new HashSet<>(); @Override public boolean getSecurityMode() { @@ -15,4 +20,19 @@ public boolean getSecurityMode() { public void setSecurityMode(boolean securityMode) { this.securityMode = securityMode; } + + @Override + public Set getExcludes() { + return excludes; + } + + @Override + public void addExclude(SecurityExclude exclude) { + excludes.add(exclude); + } + + @Override + public void removeExclude(SecurityExclude exclude) { + excludes.remove(exclude); + } } From e735bce5bffc64d87519a5cd2075e5d1544bc6cc Mon Sep 17 00:00:00 2001 From: Koshak_Mine <52908036+KoshakMineDEV@users.noreply.github.com> Date: Sat, 4 Jan 2025 18:22:21 +0300 Subject: [PATCH 05/13] feat: introduce SecurityPathAccessor --- .../security/ScriptSecurityManager.java | 2 ++ .../script/security/SecurityPathAccessor.java | 34 +++++++++++++++++++ .../impl/file/ScriptFunctionWriteFile.java | 2 +- .../security/StandardSecurityManager.java | 7 ++++ .../java/com/instancify/scriptify/Test.java | 23 +++++++++++++ 5 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 api/src/main/java/com/instancify/scriptify/api/script/security/SecurityPathAccessor.java create mode 100644 script-js-rhino/src/main/java/com/instancify/scriptify/Test.java diff --git a/api/src/main/java/com/instancify/scriptify/api/script/security/ScriptSecurityManager.java b/api/src/main/java/com/instancify/scriptify/api/script/security/ScriptSecurityManager.java index 4790ba7..0eb0d10 100644 --- a/api/src/main/java/com/instancify/scriptify/api/script/security/ScriptSecurityManager.java +++ b/api/src/main/java/com/instancify/scriptify/api/script/security/ScriptSecurityManager.java @@ -14,6 +14,8 @@ public interface ScriptSecurityManager { */ boolean getSecurityMode(); + SecurityPathAccessor getSecurityPathAccessor(); + /** * Sets the current security mode. * diff --git a/api/src/main/java/com/instancify/scriptify/api/script/security/SecurityPathAccessor.java b/api/src/main/java/com/instancify/scriptify/api/script/security/SecurityPathAccessor.java new file mode 100644 index 0000000..7002238 --- /dev/null +++ b/api/src/main/java/com/instancify/scriptify/api/script/security/SecurityPathAccessor.java @@ -0,0 +1,34 @@ +package com.instancify.scriptify.api.script.security; + +import java.nio.file.Path; + +public class SecurityPathAccessor { + private final ScriptSecurityManager securityManager; + private final Path basePath; + + public SecurityPathAccessor(final ScriptSecurityManager securityManager) { + this(securityManager, Path.of("")); + } + + public SecurityPathAccessor(final ScriptSecurityManager securityManager, Path basePath) { + this.securityManager = securityManager; + this.basePath = basePath; + } + + public Path getAccessiblePath(String path) { + if (isAcessible(path)) { + return Path.of(path); + } + + return Path.of(basePath.toString(), path.replaceAll(":\\\\", "/")); + } + + public boolean isAcessible(String path) { + /* TODO: Excludes*/ + if (securityManager.getSecurityMode()) { + return false; + } + return true; + } + +} diff --git a/core/src/main/java/com/instancify/scriptify/core/script/function/impl/file/ScriptFunctionWriteFile.java b/core/src/main/java/com/instancify/scriptify/core/script/function/impl/file/ScriptFunctionWriteFile.java index 69c881d..9bca3f7 100644 --- a/core/src/main/java/com/instancify/scriptify/core/script/function/impl/file/ScriptFunctionWriteFile.java +++ b/core/src/main/java/com/instancify/scriptify/core/script/function/impl/file/ScriptFunctionWriteFile.java @@ -27,7 +27,7 @@ public Object invoke(Script script, ScriptFunctionArgument[] args) throws Scr if (args.length == 2) { if (args[0].getValue() instanceof String filePath && args[1].getValue() instanceof String fileContent) { try { - return Files.writeString(Path.of(filePath), fileContent); + return Files.writeString(script.getSecurityManager().getSecurityPathAccessor().getAccessiblePath(filePath), fileContent); } catch (IOException e) { throw new ScriptFunctionException(e); } diff --git a/core/src/main/java/com/instancify/scriptify/core/script/security/StandardSecurityManager.java b/core/src/main/java/com/instancify/scriptify/core/script/security/StandardSecurityManager.java index 050378d..cc1ccd5 100644 --- a/core/src/main/java/com/instancify/scriptify/core/script/security/StandardSecurityManager.java +++ b/core/src/main/java/com/instancify/scriptify/core/script/security/StandardSecurityManager.java @@ -1,6 +1,7 @@ package com.instancify.scriptify.core.script.security; import com.instancify.scriptify.api.script.security.ScriptSecurityManager; +import com.instancify.scriptify.api.script.security.SecurityPathAccessor; import com.instancify.scriptify.api.script.security.exclude.SecurityExclude; import java.util.HashSet; @@ -10,12 +11,18 @@ public class StandardSecurityManager implements ScriptSecurityManager { private boolean securityMode; private final Set excludes = new HashSet<>(); + private final SecurityPathAccessor pathAccessor = new SecurityPathAccessor(this); @Override public boolean getSecurityMode() { return securityMode; } + @Override + public SecurityPathAccessor getSecurityPathAccessor() { + return pathAccessor; + } + @Override public void setSecurityMode(boolean securityMode) { this.securityMode = securityMode; diff --git a/script-js-rhino/src/main/java/com/instancify/scriptify/Test.java b/script-js-rhino/src/main/java/com/instancify/scriptify/Test.java new file mode 100644 index 0000000..4210ccf --- /dev/null +++ b/script-js-rhino/src/main/java/com/instancify/scriptify/Test.java @@ -0,0 +1,23 @@ +package com.instancify.scriptify; + +import com.instancify.scriptify.api.exception.ScriptException; +import com.instancify.scriptify.core.script.constant.StandardConstantManager; +import com.instancify.scriptify.core.script.function.StandardFunctionManager; +import com.instancify.scriptify.script.JsScript; + +public class Test { + public static void main(String[] args) { + JsScript script = new JsScript(); + script.setConstantManager(new StandardConstantManager()); + script.setFunctionManager(new StandardFunctionManager()); + script.getSecurityManager().setSecurityMode(true); + + try { + script.eval(""" + writeFile(normalizePath("E:\\\\penis.txt"), "asdsas"); + """); + } catch (ScriptException e) { + throw new RuntimeException(e); + } + } +} From 20b61ea077a66b3538f38c7b850e80dff48dd908 Mon Sep 17 00:00:00 2001 From: Koshak_Mine <52908036+KoshakMineDEV@users.noreply.github.com> Date: Sat, 4 Jan 2025 18:36:29 +0300 Subject: [PATCH 06/13] fix: getAccessiblePath() --- .../scriptify/api/script/security/SecurityPathAccessor.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/com/instancify/scriptify/api/script/security/SecurityPathAccessor.java b/api/src/main/java/com/instancify/scriptify/api/script/security/SecurityPathAccessor.java index 7002238..29e9f33 100644 --- a/api/src/main/java/com/instancify/scriptify/api/script/security/SecurityPathAccessor.java +++ b/api/src/main/java/com/instancify/scriptify/api/script/security/SecurityPathAccessor.java @@ -1,13 +1,14 @@ package com.instancify.scriptify.api.script.security; import java.nio.file.Path; +import java.nio.file.Paths; public class SecurityPathAccessor { private final ScriptSecurityManager securityManager; private final Path basePath; public SecurityPathAccessor(final ScriptSecurityManager securityManager) { - this(securityManager, Path.of("")); + this(securityManager, Paths.get("").toAbsolutePath()); } public SecurityPathAccessor(final ScriptSecurityManager securityManager, Path basePath) { @@ -20,7 +21,7 @@ public Path getAccessiblePath(String path) { return Path.of(path); } - return Path.of(basePath.toString(), path.replaceAll(":\\\\", "/")); + return Path.of(basePath.toString(), path.replaceAll(":", "")); } public boolean isAcessible(String path) { From 60a91034d39ad89dfd476dee3028a30c180c07d1 Mon Sep 17 00:00:00 2001 From: MEFRREEX Date: Sat, 4 Jan 2025 17:47:46 +0200 Subject: [PATCH 07/13] refactor: small refactor + docs(javadoc): added javadoc for SecurityPathAccessor --- .../security/ScriptSecurityManager.java | 9 ++- .../script/security/SecurityPathAccessor.java | 58 ++++++++++++++++--- .../security/exclude/SecurityExclude.java | 1 - .../impl/file/ScriptFunctionWriteFile.java | 3 +- .../security/StandardSecurityManager.java | 8 +-- 5 files changed, 63 insertions(+), 16 deletions(-) diff --git a/api/src/main/java/com/instancify/scriptify/api/script/security/ScriptSecurityManager.java b/api/src/main/java/com/instancify/scriptify/api/script/security/ScriptSecurityManager.java index 0eb0d10..ed6c3bd 100644 --- a/api/src/main/java/com/instancify/scriptify/api/script/security/ScriptSecurityManager.java +++ b/api/src/main/java/com/instancify/scriptify/api/script/security/ScriptSecurityManager.java @@ -14,8 +14,6 @@ public interface ScriptSecurityManager { */ boolean getSecurityMode(); - SecurityPathAccessor getSecurityPathAccessor(); - /** * Sets the current security mode. * @@ -24,6 +22,13 @@ public interface ScriptSecurityManager { */ void setSecurityMode(boolean securityMode); + /** + * Receives security path accessor. + * + * @return Security path accessor + */ + SecurityPathAccessor getPathAccessor(); + /** * Retrieves all existing exclusions for this script. * diff --git a/api/src/main/java/com/instancify/scriptify/api/script/security/SecurityPathAccessor.java b/api/src/main/java/com/instancify/scriptify/api/script/security/SecurityPathAccessor.java index 29e9f33..6ee9652 100644 --- a/api/src/main/java/com/instancify/scriptify/api/script/security/SecurityPathAccessor.java +++ b/api/src/main/java/com/instancify/scriptify/api/script/security/SecurityPathAccessor.java @@ -3,33 +3,77 @@ import java.nio.file.Path; import java.nio.file.Paths; +/** + * Manages path access based on security constraints, ensuring only safe paths are accessible. + */ public class SecurityPathAccessor { + private final ScriptSecurityManager securityManager; - private final Path basePath; + private Path basePath; - public SecurityPathAccessor(final ScriptSecurityManager securityManager) { + /** + * Constructs a SecurityPathAccessor with the default base path set to the current working directory. + * + * @param securityManager The security manager to check access permissions + */ + public SecurityPathAccessor(ScriptSecurityManager securityManager) { this(securityManager, Paths.get("").toAbsolutePath()); } - public SecurityPathAccessor(final ScriptSecurityManager securityManager, Path basePath) { + /** + * Constructs a SecurityPathAccessor with a specified base path for relative path calculations. + * + * @param securityManager The security manager to check access permissions + * @param basePath The base path from which relative paths are calculated + */ + public SecurityPathAccessor(ScriptSecurityManager securityManager, Path basePath) { this.securityManager = securityManager; this.basePath = basePath; } + /** + * Gets a base path for this accessor, which will be used for relative path calculations. + * + * @return The base path to set + */ + public Path getBasePath() { + return basePath; + } + + /** + * Sets a new base path for this accessor, which will be used for relative path calculations. + * + * @param basePath The new base path to set + */ + public void setBasePath(Path basePath) { + this.basePath = basePath; + } + + /** + * Returns a path that is safe to access according to security rules. If the path is not accessible, + * it returns a path relative to the base path with ':' characters removed to prevent potential path traversal attacks. + * + * @param path The path string to be checked and possibly modified + * @return A Path object representing the accessible path or a sanitized version if not accessible + */ public Path getAccessiblePath(String path) { - if (isAcessible(path)) { + if (this.isAccessible(path)) { return Path.of(path); } - return Path.of(basePath.toString(), path.replaceAll(":", "")); } - public boolean isAcessible(String path) { + /** + * Checks if the given path is accessible based on the current security settings. + * + * @param path The path to check for access permission + * @return true if the path is accessible, false otherwise + */ + public boolean isAccessible(String path) { /* TODO: Excludes*/ if (securityManager.getSecurityMode()) { return false; } return true; } - } diff --git a/api/src/main/java/com/instancify/scriptify/api/script/security/exclude/SecurityExclude.java b/api/src/main/java/com/instancify/scriptify/api/script/security/exclude/SecurityExclude.java index 2c477f4..90439e4 100644 --- a/api/src/main/java/com/instancify/scriptify/api/script/security/exclude/SecurityExclude.java +++ b/api/src/main/java/com/instancify/scriptify/api/script/security/exclude/SecurityExclude.java @@ -9,7 +9,6 @@ public interface SecurityExclude { */ String getValue(); - /** * Checks that the value of path or packet is excluded. * diff --git a/core/src/main/java/com/instancify/scriptify/core/script/function/impl/file/ScriptFunctionWriteFile.java b/core/src/main/java/com/instancify/scriptify/core/script/function/impl/file/ScriptFunctionWriteFile.java index 9bca3f7..8b84678 100644 --- a/core/src/main/java/com/instancify/scriptify/core/script/function/impl/file/ScriptFunctionWriteFile.java +++ b/core/src/main/java/com/instancify/scriptify/core/script/function/impl/file/ScriptFunctionWriteFile.java @@ -10,7 +10,6 @@ import java.io.IOException; import java.nio.file.Files; -import java.nio.file.Path; /** * Represents a function to write the contents of a file @@ -27,7 +26,7 @@ public Object invoke(Script script, ScriptFunctionArgument[] args) throws Scr if (args.length == 2) { if (args[0].getValue() instanceof String filePath && args[1].getValue() instanceof String fileContent) { try { - return Files.writeString(script.getSecurityManager().getSecurityPathAccessor().getAccessiblePath(filePath), fileContent); + return Files.writeString(script.getSecurityManager().getPathAccessor().getAccessiblePath(filePath), fileContent); } catch (IOException e) { throw new ScriptFunctionException(e); } diff --git a/core/src/main/java/com/instancify/scriptify/core/script/security/StandardSecurityManager.java b/core/src/main/java/com/instancify/scriptify/core/script/security/StandardSecurityManager.java index cc1ccd5..86d5839 100644 --- a/core/src/main/java/com/instancify/scriptify/core/script/security/StandardSecurityManager.java +++ b/core/src/main/java/com/instancify/scriptify/core/script/security/StandardSecurityManager.java @@ -19,13 +19,13 @@ public boolean getSecurityMode() { } @Override - public SecurityPathAccessor getSecurityPathAccessor() { - return pathAccessor; + public void setSecurityMode(boolean securityMode) { + this.securityMode = securityMode; } @Override - public void setSecurityMode(boolean securityMode) { - this.securityMode = securityMode; + public SecurityPathAccessor getPathAccessor() { + return pathAccessor; } @Override From e68d8c12cfca8e7e2c53bd047018570d3476c79c Mon Sep 17 00:00:00 2001 From: MEFRREEX Date: Sun, 5 Jan 2025 18:33:24 +0200 Subject: [PATCH 08/13] fix: fixed PathSecurityExclude + path exclusions for SecurityPathAccessor --- .../script/security/SecurityPathAccessor.java | 20 ++++++++++++---- .../security/exclude/PathSecurityExclude.java | 5 ---- .../security/exclude/SecurityExclude.java | 3 ++- .../java/com/instancify/scriptify/Test.java | 23 ------------------- 4 files changed, 18 insertions(+), 33 deletions(-) delete mode 100644 script-js-rhino/src/main/java/com/instancify/scriptify/Test.java diff --git a/api/src/main/java/com/instancify/scriptify/api/script/security/SecurityPathAccessor.java b/api/src/main/java/com/instancify/scriptify/api/script/security/SecurityPathAccessor.java index 6ee9652..1237260 100644 --- a/api/src/main/java/com/instancify/scriptify/api/script/security/SecurityPathAccessor.java +++ b/api/src/main/java/com/instancify/scriptify/api/script/security/SecurityPathAccessor.java @@ -1,5 +1,8 @@ package com.instancify.scriptify.api.script.security; +import com.instancify.scriptify.api.script.security.exclude.PathSecurityExclude; +import com.instancify.scriptify.api.script.security.exclude.SecurityExclude; + import java.nio.file.Path; import java.nio.file.Paths; @@ -70,10 +73,19 @@ public Path getAccessiblePath(String path) { * @return true if the path is accessible, false otherwise */ public boolean isAccessible(String path) { - /* TODO: Excludes*/ - if (securityManager.getSecurityMode()) { - return false; + if (!securityManager.getSecurityMode()) { + return true; + } + + // Search all exclusions and check that the path is excluded + for (SecurityExclude exclude : securityManager.getExcludes()) { + if (exclude instanceof PathSecurityExclude) { + if (exclude.isExcluded(path)) { + return true; + } + } } - return true; + + return false; } } diff --git a/api/src/main/java/com/instancify/scriptify/api/script/security/exclude/PathSecurityExclude.java b/api/src/main/java/com/instancify/scriptify/api/script/security/exclude/PathSecurityExclude.java index 27fe7a1..0c5e516 100644 --- a/api/src/main/java/com/instancify/scriptify/api/script/security/exclude/PathSecurityExclude.java +++ b/api/src/main/java/com/instancify/scriptify/api/script/security/exclude/PathSecurityExclude.java @@ -12,9 +12,4 @@ public PathSecurityExclude(String value) { public String getValue() { return value; } - - @Override - public boolean isExcluded(String value) { - return this.getValue().startsWith(value); - } } diff --git a/api/src/main/java/com/instancify/scriptify/api/script/security/exclude/SecurityExclude.java b/api/src/main/java/com/instancify/scriptify/api/script/security/exclude/SecurityExclude.java index 90439e4..0407f8a 100644 --- a/api/src/main/java/com/instancify/scriptify/api/script/security/exclude/SecurityExclude.java +++ b/api/src/main/java/com/instancify/scriptify/api/script/security/exclude/SecurityExclude.java @@ -16,7 +16,8 @@ public interface SecurityExclude { * @return True if excluded, otherwise false */ default boolean isExcluded(String value) { - return this.getValue().startsWith(value); + // Check that the path starts from the path specified in the exclusion + return value.startsWith(this.getValue()); } /** diff --git a/script-js-rhino/src/main/java/com/instancify/scriptify/Test.java b/script-js-rhino/src/main/java/com/instancify/scriptify/Test.java deleted file mode 100644 index 4210ccf..0000000 --- a/script-js-rhino/src/main/java/com/instancify/scriptify/Test.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.instancify.scriptify; - -import com.instancify.scriptify.api.exception.ScriptException; -import com.instancify.scriptify.core.script.constant.StandardConstantManager; -import com.instancify.scriptify.core.script.function.StandardFunctionManager; -import com.instancify.scriptify.script.JsScript; - -public class Test { - public static void main(String[] args) { - JsScript script = new JsScript(); - script.setConstantManager(new StandardConstantManager()); - script.setFunctionManager(new StandardFunctionManager()); - script.getSecurityManager().setSecurityMode(true); - - try { - script.eval(""" - writeFile(normalizePath("E:\\\\penis.txt"), "asdsas"); - """); - } catch (ScriptException e) { - throw new RuntimeException(e); - } - } -} From 7ac3b5c1af779f108f374e4425d60cc3953b564b Mon Sep 17 00:00:00 2001 From: MEFRREEX Date: Sun, 5 Jan 2025 18:39:38 +0200 Subject: [PATCH 09/13] refactor: refactor execCommand function --- .../impl/os/ScriptFunctionExecCommand.java | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/core/src/main/java/com/instancify/scriptify/core/script/function/impl/os/ScriptFunctionExecCommand.java b/core/src/main/java/com/instancify/scriptify/core/script/function/impl/os/ScriptFunctionExecCommand.java index 1a8c884..bb9cef3 100644 --- a/core/src/main/java/com/instancify/scriptify/core/script/function/impl/os/ScriptFunctionExecCommand.java +++ b/core/src/main/java/com/instancify/scriptify/core/script/function/impl/os/ScriptFunctionExecCommand.java @@ -31,23 +31,23 @@ public Object invoke(Script script, ScriptFunctionArgument[] args) throws Scr if (!(args[0].getValue() instanceof String input)) { throw new ScriptFunctionArgTypeException(String.class, args[0].getType()); } + try { Process process = Runtime.getRuntime().exec(input); - BufferedReader stdInput = new BufferedReader(new - InputStreamReader(process.getInputStream())); - BufferedReader stdError = new BufferedReader(new - InputStreamReader(process.getErrorStream())); - - String message = ""; - String buff = null; - while ((buff = stdInput.readLine()) != null) { - message += buff + "\n"; - } - while ((buff = stdError.readLine()) != null) { - message += buff + "\n"; - } - return message; + StringBuilder message = new StringBuilder(); + try (BufferedReader stdInput = new BufferedReader(new InputStreamReader(process.getInputStream())); + BufferedReader stdError = new BufferedReader(new InputStreamReader(process.getErrorStream()))) { + + String line; + while ((line = stdInput.readLine()) != null) { + message.append(line).append("\n"); + } + while ((line = stdError.readLine()) != null) { + message.append(line).append("\n"); + } + } + return message.toString(); } catch (IOException e) { throw new ScriptFunctionException(e); } From fb2691c3200bf20a36ecdac67743718734176a86 Mon Sep 17 00:00:00 2001 From: Koshak_Mine <52908036+KoshakMineDEV@users.noreply.github.com> Date: Wed, 8 Jan 2025 20:01:29 +0300 Subject: [PATCH 10/13] feat: incomplete ClassSecurityExclude --- .../exclude/ClassSecurityExclude.java | 15 ++++++++++ .../exclude/PackageSecurityExclude.java | 15 ---------- .../security/exclude/SecurityExclude.java | 6 ++-- .../instancify/scriptify/script/JsScript.java | 24 +++++++++++++-- .../scriptify/script/SafeClassShutter.java | 26 +++++++++++++++++ .../com/instancify/scriptify/script/Test.java | 29 +++++++++++++++++++ 6 files changed, 95 insertions(+), 20 deletions(-) create mode 100644 api/src/main/java/com/instancify/scriptify/api/script/security/exclude/ClassSecurityExclude.java delete mode 100644 api/src/main/java/com/instancify/scriptify/api/script/security/exclude/PackageSecurityExclude.java create mode 100644 script-js-rhino/src/main/java/com/instancify/scriptify/script/SafeClassShutter.java create mode 100644 script-js-rhino/src/main/java/com/instancify/scriptify/script/Test.java diff --git a/api/src/main/java/com/instancify/scriptify/api/script/security/exclude/ClassSecurityExclude.java b/api/src/main/java/com/instancify/scriptify/api/script/security/exclude/ClassSecurityExclude.java new file mode 100644 index 0000000..118c770 --- /dev/null +++ b/api/src/main/java/com/instancify/scriptify/api/script/security/exclude/ClassSecurityExclude.java @@ -0,0 +1,15 @@ +package com.instancify.scriptify.api.script.security.exclude; + +public class ClassSecurityExclude implements SecurityExclude { + + private final Class value; + + public ClassSecurityExclude(Class value) { + this.value = value; + } + + @Override + public String getValue() { + return value.getName(); + } +} diff --git a/api/src/main/java/com/instancify/scriptify/api/script/security/exclude/PackageSecurityExclude.java b/api/src/main/java/com/instancify/scriptify/api/script/security/exclude/PackageSecurityExclude.java deleted file mode 100644 index d356e12..0000000 --- a/api/src/main/java/com/instancify/scriptify/api/script/security/exclude/PackageSecurityExclude.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.instancify.scriptify.api.script.security.exclude; - -public class PackageSecurityExclude implements SecurityExclude { - - private final String value; - - public PackageSecurityExclude(String value) { - this.value = value; - } - - @Override - public String getValue() { - return value; - } -} diff --git a/api/src/main/java/com/instancify/scriptify/api/script/security/exclude/SecurityExclude.java b/api/src/main/java/com/instancify/scriptify/api/script/security/exclude/SecurityExclude.java index 0407f8a..501b35d 100644 --- a/api/src/main/java/com/instancify/scriptify/api/script/security/exclude/SecurityExclude.java +++ b/api/src/main/java/com/instancify/scriptify/api/script/security/exclude/SecurityExclude.java @@ -26,9 +26,9 @@ default boolean isExcluded(String value) { * @param value A package that will be excluded * @return A new exclusion instance for the package */ - static PackageSecurityExclude ofPackage(String value) { - return new PackageSecurityExclude(value); - } + /*static ClassSecurityExclude ofPackage(String value) { + return new ClassSecurityExclude(value); + }*/ /** * Creates a new exclusion instance for the path. diff --git a/script-js-rhino/src/main/java/com/instancify/scriptify/script/JsScript.java b/script-js-rhino/src/main/java/com/instancify/scriptify/script/JsScript.java index f1fdbe0..bceb64c 100644 --- a/script-js-rhino/src/main/java/com/instancify/scriptify/script/JsScript.java +++ b/script-js-rhino/src/main/java/com/instancify/scriptify/script/JsScript.java @@ -7,14 +7,16 @@ import com.instancify.scriptify.api.script.function.ScriptFunction; import com.instancify.scriptify.api.script.function.ScriptFunctionManager; import com.instancify.scriptify.api.script.security.ScriptSecurityManager; +import com.instancify.scriptify.api.script.security.exclude.ClassSecurityExclude; +import com.instancify.scriptify.api.script.security.exclude.SecurityExclude; import com.instancify.scriptify.core.script.security.StandardSecurityManager; import org.mozilla.javascript.Context; +import org.mozilla.javascript.NativeJavaClass; import org.mozilla.javascript.ScriptableObject; public class JsScript implements Script { private final Context context = Context.enter(); - private final ScriptableObject scope = context.initStandardObjects(); private final ScriptSecurityManager securityManager = new StandardSecurityManager(); private ScriptFunctionManager functionManager; private ScriptConstantManager constantManager; @@ -46,6 +48,21 @@ public void setConstantManager(ScriptConstantManager constantManager) { @Override public Object eval(String script) throws ScriptException { + ScriptableObject scope; + + if(securityManager.getSecurityMode()){ + SafeClassShutter safeClassShutter = new SafeClassShutter(); + context.setClassShutter(safeClassShutter); + + for(SecurityExclude exclude : securityManager.getExcludes()) { + if(exclude instanceof ClassSecurityExclude classExclude) { + safeClassShutter.allowedClasses.add(classExclude.getValue()); + } + } + } + + scope = context.initStandardObjects(); + if (functionManager != null) { for (ScriptFunction function : functionManager.getFunctions().values()) { scope.put(function.getName(), scope, new JsFunction(this, function)); @@ -59,8 +76,11 @@ public Object eval(String script) throws ScriptException { } try { - return context.evaluateString(scope, script, null, 1, null); + Object value = context.evaluateString(scope, script, null, 1, null); + context.close(); + return value; } catch (Exception e) { + context.close(); throw new ScriptException(e); } } diff --git a/script-js-rhino/src/main/java/com/instancify/scriptify/script/SafeClassShutter.java b/script-js-rhino/src/main/java/com/instancify/scriptify/script/SafeClassShutter.java new file mode 100644 index 0000000..4d66593 --- /dev/null +++ b/script-js-rhino/src/main/java/com/instancify/scriptify/script/SafeClassShutter.java @@ -0,0 +1,26 @@ +package com.instancify.scriptify.script; + +import org.mozilla.javascript.ClassShutter; +import org.mozilla.javascript.EcmaError; + +import java.util.HashSet; +import java.util.Set; + +public class SafeClassShutter implements ClassShutter { + public final Set allowedClasses; + + @Override + public boolean visibleToScripts(final String fullClassName) { + boolean _startsWith = fullClassName.startsWith("adapter"); + if (_startsWith) { + return true; + } + return this.allowedClasses.contains(fullClassName); + } + + public SafeClassShutter() { + HashSet _hashSet = new HashSet(); + this.allowedClasses = _hashSet; + this.allowedClasses.add(EcmaError.class.getName()); + } +} diff --git a/script-js-rhino/src/main/java/com/instancify/scriptify/script/Test.java b/script-js-rhino/src/main/java/com/instancify/scriptify/script/Test.java new file mode 100644 index 0000000..7102d1b --- /dev/null +++ b/script-js-rhino/src/main/java/com/instancify/scriptify/script/Test.java @@ -0,0 +1,29 @@ +package com.instancify.scriptify.script; + +import com.instancify.scriptify.api.exception.ScriptException; +import com.instancify.scriptify.api.script.security.exclude.ClassSecurityExclude; +import com.instancify.scriptify.core.script.constant.StandardConstantManager; +import com.instancify.scriptify.core.script.function.StandardFunctionManager; + +import java.io.File; + +public class Test { + public static void main(String[] args) { + + JsScript script = new JsScript(); + script.setFunctionManager(new StandardFunctionManager()); + script.setConstantManager(new StandardConstantManager()); + script.getSecurityManager().setSecurityMode(true); + script.getSecurityManager().addExclude(new ClassSecurityExclude(File.class)); + + try { + script.eval(""" + const test = new java.io.File("test.txt"); + + """); + } catch(ScriptException e) { + throw new RuntimeException(e); + } + + } +} From ed1eb8cd9ea4221ac442da4665b033ba1cd9d8d6 Mon Sep 17 00:00:00 2001 From: MEFRREEX Date: Wed, 8 Jan 2025 20:02:26 +0200 Subject: [PATCH 11/13] refactor: added api interface for SecurityPathAccessor + add SecurityClassAccessor and rename SafeClassShutter to JsSecurityClassAccessor --- .../security/SecurityClassAccessor.java | 23 +++++ .../script/security/SecurityPathAccessor.java | 61 +----------- .../security/SecurityPathAccessorImpl.java | 97 +++++++++++++++++++ .../security/StandardSecurityManager.java | 2 +- .../instancify/scriptify/script/JsScript.java | 27 +++--- .../script/JsSecurityClassAccessor.java | 32 ++++++ .../scriptify/script/SafeClassShutter.java | 26 ----- .../com/instancify/scriptify/script/Test.java | 29 ------ 8 files changed, 170 insertions(+), 127 deletions(-) create mode 100644 api/src/main/java/com/instancify/scriptify/api/script/security/SecurityClassAccessor.java create mode 100644 core/src/main/java/com/instancify/scriptify/core/script/security/SecurityPathAccessorImpl.java create mode 100644 script-js-rhino/src/main/java/com/instancify/scriptify/script/JsSecurityClassAccessor.java delete mode 100644 script-js-rhino/src/main/java/com/instancify/scriptify/script/SafeClassShutter.java delete mode 100644 script-js-rhino/src/main/java/com/instancify/scriptify/script/Test.java diff --git a/api/src/main/java/com/instancify/scriptify/api/script/security/SecurityClassAccessor.java b/api/src/main/java/com/instancify/scriptify/api/script/security/SecurityClassAccessor.java new file mode 100644 index 0000000..a8f4a26 --- /dev/null +++ b/api/src/main/java/com/instancify/scriptify/api/script/security/SecurityClassAccessor.java @@ -0,0 +1,23 @@ +package com.instancify.scriptify.api.script.security; + +import java.util.Set; + +/** + * Manages class access based on security constraints, ensuring only allowed classes can be used. + */ +public interface SecurityClassAccessor { + + /** + * Retrieves the set of class names that are allowed to be accessed or used. + * + * @return A set of strings representing the names of allowed classes + */ + Set getAllowedClasses(); + + /** + * Adds a class to the list of allowed classes, which can then be used or accessed. + * + * @param allowedClass The name of the class to be added to the allowed list + */ + void addAllowedClass(String allowedClass); +} \ No newline at end of file diff --git a/api/src/main/java/com/instancify/scriptify/api/script/security/SecurityPathAccessor.java b/api/src/main/java/com/instancify/scriptify/api/script/security/SecurityPathAccessor.java index 1237260..1a11b16 100644 --- a/api/src/main/java/com/instancify/scriptify/api/script/security/SecurityPathAccessor.java +++ b/api/src/main/java/com/instancify/scriptify/api/script/security/SecurityPathAccessor.java @@ -1,56 +1,25 @@ package com.instancify.scriptify.api.script.security; -import com.instancify.scriptify.api.script.security.exclude.PathSecurityExclude; -import com.instancify.scriptify.api.script.security.exclude.SecurityExclude; - import java.nio.file.Path; -import java.nio.file.Paths; /** * Manages path access based on security constraints, ensuring only safe paths are accessible. */ -public class SecurityPathAccessor { - - private final ScriptSecurityManager securityManager; - private Path basePath; - - /** - * Constructs a SecurityPathAccessor with the default base path set to the current working directory. - * - * @param securityManager The security manager to check access permissions - */ - public SecurityPathAccessor(ScriptSecurityManager securityManager) { - this(securityManager, Paths.get("").toAbsolutePath()); - } - - /** - * Constructs a SecurityPathAccessor with a specified base path for relative path calculations. - * - * @param securityManager The security manager to check access permissions - * @param basePath The base path from which relative paths are calculated - */ - public SecurityPathAccessor(ScriptSecurityManager securityManager, Path basePath) { - this.securityManager = securityManager; - this.basePath = basePath; - } +public interface SecurityPathAccessor { /** * Gets a base path for this accessor, which will be used for relative path calculations. * * @return The base path to set */ - public Path getBasePath() { - return basePath; - } + Path getBasePath(); /** * Sets a new base path for this accessor, which will be used for relative path calculations. * * @param basePath The new base path to set */ - public void setBasePath(Path basePath) { - this.basePath = basePath; - } + void setBasePath(Path basePath); /** * Returns a path that is safe to access according to security rules. If the path is not accessible, @@ -59,12 +28,7 @@ public void setBasePath(Path basePath) { * @param path The path string to be checked and possibly modified * @return A Path object representing the accessible path or a sanitized version if not accessible */ - public Path getAccessiblePath(String path) { - if (this.isAccessible(path)) { - return Path.of(path); - } - return Path.of(basePath.toString(), path.replaceAll(":", "")); - } + Path getAccessiblePath(String path); /** * Checks if the given path is accessible based on the current security settings. @@ -72,20 +36,5 @@ public Path getAccessiblePath(String path) { * @param path The path to check for access permission * @return true if the path is accessible, false otherwise */ - public boolean isAccessible(String path) { - if (!securityManager.getSecurityMode()) { - return true; - } - - // Search all exclusions and check that the path is excluded - for (SecurityExclude exclude : securityManager.getExcludes()) { - if (exclude instanceof PathSecurityExclude) { - if (exclude.isExcluded(path)) { - return true; - } - } - } - - return false; - } + boolean isAccessible(String path); } diff --git a/core/src/main/java/com/instancify/scriptify/core/script/security/SecurityPathAccessorImpl.java b/core/src/main/java/com/instancify/scriptify/core/script/security/SecurityPathAccessorImpl.java new file mode 100644 index 0000000..3fd1bfd --- /dev/null +++ b/core/src/main/java/com/instancify/scriptify/core/script/security/SecurityPathAccessorImpl.java @@ -0,0 +1,97 @@ +package com.instancify.scriptify.core.script.security; + +import com.instancify.scriptify.api.script.security.ScriptSecurityManager; +import com.instancify.scriptify.api.script.security.SecurityPathAccessor; +import com.instancify.scriptify.api.script.security.exclude.PathSecurityExclude; +import com.instancify.scriptify.api.script.security.exclude.SecurityExclude; + +import java.nio.file.Path; +import java.nio.file.Paths; + +/** + * Manages path access based on security constraints, ensuring only safe paths are accessible. + */ +public class SecurityPathAccessorImpl implements SecurityPathAccessor { + + private final ScriptSecurityManager securityManager; + private Path basePath; + + /** + * Constructs a SecurityPathAccessor with the default base path set to the current working directory. + * + * @param securityManager The security manager to check access permissions + */ + public SecurityPathAccessorImpl(ScriptSecurityManager securityManager) { + this(securityManager, Paths.get("").toAbsolutePath()); + } + + /** + * Constructs a SecurityPathAccessor with a specified base path for relative path calculations. + * + * @param securityManager The security manager to check access permissions + * @param basePath The base path from which relative paths are calculated + */ + public SecurityPathAccessorImpl(ScriptSecurityManager securityManager, Path basePath) { + this.securityManager = securityManager; + this.basePath = basePath; + } + + /** + * Gets a base path for this accessor, which will be used for relative path calculations. + * + * @return The base path to set + */ + @Override + public Path getBasePath() { + return basePath; + } + + /** + * Sets a new base path for this accessor, which will be used for relative path calculations. + * + * @param basePath The new base path to set + */ + @Override + public void setBasePath(Path basePath) { + this.basePath = basePath; + } + + /** + * Returns a path that is safe to access according to security rules. If the path is not accessible, + * it returns a path relative to the base path with ':' characters removed to prevent potential path traversal attacks. + * + * @param path The path string to be checked and possibly modified + * @return A Path object representing the accessible path or a sanitized version if not accessible + */ + @Override + public Path getAccessiblePath(String path) { + if (this.isAccessible(path)) { + return Path.of(path); + } + return Path.of(basePath.toString(), path.replaceAll(":", "")); + } + + /** + * Checks if the given path is accessible based on the current security settings. + * + * @param path The path to check for access permission + * @return true if the path is accessible, false otherwise + */ + @Override + public boolean isAccessible(String path) { + if (!securityManager.getSecurityMode()) { + return true; + } + + // Search all exclusions and check that the path is excluded + for (SecurityExclude exclude : securityManager.getExcludes()) { + if (exclude instanceof PathSecurityExclude) { + if (exclude.isExcluded(path)) { + return true; + } + } + } + + return false; + } +} diff --git a/core/src/main/java/com/instancify/scriptify/core/script/security/StandardSecurityManager.java b/core/src/main/java/com/instancify/scriptify/core/script/security/StandardSecurityManager.java index 86d5839..20dbb66 100644 --- a/core/src/main/java/com/instancify/scriptify/core/script/security/StandardSecurityManager.java +++ b/core/src/main/java/com/instancify/scriptify/core/script/security/StandardSecurityManager.java @@ -11,7 +11,7 @@ public class StandardSecurityManager implements ScriptSecurityManager { private boolean securityMode; private final Set excludes = new HashSet<>(); - private final SecurityPathAccessor pathAccessor = new SecurityPathAccessor(this); + private final SecurityPathAccessor pathAccessor = new SecurityPathAccessorImpl(this); @Override public boolean getSecurityMode() { diff --git a/script-js-rhino/src/main/java/com/instancify/scriptify/script/JsScript.java b/script-js-rhino/src/main/java/com/instancify/scriptify/script/JsScript.java index bceb64c..031b80f 100644 --- a/script-js-rhino/src/main/java/com/instancify/scriptify/script/JsScript.java +++ b/script-js-rhino/src/main/java/com/instancify/scriptify/script/JsScript.java @@ -11,7 +11,6 @@ import com.instancify.scriptify.api.script.security.exclude.SecurityExclude; import com.instancify.scriptify.core.script.security.StandardSecurityManager; import org.mozilla.javascript.Context; -import org.mozilla.javascript.NativeJavaClass; import org.mozilla.javascript.ScriptableObject; public class JsScript implements Script { @@ -48,21 +47,20 @@ public void setConstantManager(ScriptConstantManager constantManager) { @Override public Object eval(String script) throws ScriptException { - ScriptableObject scope; + ScriptableObject scope = context.initStandardObjects(); - if(securityManager.getSecurityMode()){ - SafeClassShutter safeClassShutter = new SafeClassShutter(); - context.setClassShutter(safeClassShutter); - - for(SecurityExclude exclude : securityManager.getExcludes()) { - if(exclude instanceof ClassSecurityExclude classExclude) { - safeClassShutter.allowedClasses.add(classExclude.getValue()); + // If security mode is enabled, search all exclusions + // and add the classes that were excluded to JsSafeClassShutter + if (securityManager.getSecurityMode()) { + JsSecurityClassAccessor classAccessor = new JsSecurityClassAccessor(); + for (SecurityExclude exclude : securityManager.getExcludes()) { + if (exclude instanceof ClassSecurityExclude classExclude) { + classAccessor.addAllowedClass(classExclude.getValue()); } } + context.setClassShutter(classAccessor); } - scope = context.initStandardObjects(); - if (functionManager != null) { for (ScriptFunction function : functionManager.getFunctions().values()) { scope.put(function.getName(), scope, new JsFunction(this, function)); @@ -76,12 +74,11 @@ public Object eval(String script) throws ScriptException { } try { - Object value = context.evaluateString(scope, script, null, 1, null); - context.close(); - return value; + return context.evaluateString(scope, script, null, 1, null); } catch (Exception e) { - context.close(); throw new ScriptException(e); + } finally { + context.close(); } } } diff --git a/script-js-rhino/src/main/java/com/instancify/scriptify/script/JsSecurityClassAccessor.java b/script-js-rhino/src/main/java/com/instancify/scriptify/script/JsSecurityClassAccessor.java new file mode 100644 index 0000000..f94ed7f --- /dev/null +++ b/script-js-rhino/src/main/java/com/instancify/scriptify/script/JsSecurityClassAccessor.java @@ -0,0 +1,32 @@ +package com.instancify.scriptify.script; + +import com.instancify.scriptify.api.script.security.SecurityClassAccessor; +import org.mozilla.javascript.ClassShutter; +import org.mozilla.javascript.EcmaError; + +import java.util.HashSet; +import java.util.Set; + +public class JsSecurityClassAccessor implements ClassShutter, SecurityClassAccessor { + + private final Set allowedClasses = new HashSet<>(); + + public JsSecurityClassAccessor() { + this.allowedClasses.add(EcmaError.class.getName()); + } + + @Override + public Set getAllowedClasses() { + return allowedClasses; + } + + @Override + public void addAllowedClass(String allowedClass) { + this.allowedClasses.add(allowedClass); + } + + @Override + public boolean visibleToScripts(String fullClassName) { + return this.allowedClasses.contains(fullClassName); + } +} diff --git a/script-js-rhino/src/main/java/com/instancify/scriptify/script/SafeClassShutter.java b/script-js-rhino/src/main/java/com/instancify/scriptify/script/SafeClassShutter.java deleted file mode 100644 index 4d66593..0000000 --- a/script-js-rhino/src/main/java/com/instancify/scriptify/script/SafeClassShutter.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.instancify.scriptify.script; - -import org.mozilla.javascript.ClassShutter; -import org.mozilla.javascript.EcmaError; - -import java.util.HashSet; -import java.util.Set; - -public class SafeClassShutter implements ClassShutter { - public final Set allowedClasses; - - @Override - public boolean visibleToScripts(final String fullClassName) { - boolean _startsWith = fullClassName.startsWith("adapter"); - if (_startsWith) { - return true; - } - return this.allowedClasses.contains(fullClassName); - } - - public SafeClassShutter() { - HashSet _hashSet = new HashSet(); - this.allowedClasses = _hashSet; - this.allowedClasses.add(EcmaError.class.getName()); - } -} diff --git a/script-js-rhino/src/main/java/com/instancify/scriptify/script/Test.java b/script-js-rhino/src/main/java/com/instancify/scriptify/script/Test.java deleted file mode 100644 index 7102d1b..0000000 --- a/script-js-rhino/src/main/java/com/instancify/scriptify/script/Test.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.instancify.scriptify.script; - -import com.instancify.scriptify.api.exception.ScriptException; -import com.instancify.scriptify.api.script.security.exclude.ClassSecurityExclude; -import com.instancify.scriptify.core.script.constant.StandardConstantManager; -import com.instancify.scriptify.core.script.function.StandardFunctionManager; - -import java.io.File; - -public class Test { - public static void main(String[] args) { - - JsScript script = new JsScript(); - script.setFunctionManager(new StandardFunctionManager()); - script.setConstantManager(new StandardConstantManager()); - script.getSecurityManager().setSecurityMode(true); - script.getSecurityManager().addExclude(new ClassSecurityExclude(File.class)); - - try { - script.eval(""" - const test = new java.io.File("test.txt"); - - """); - } catch(ScriptException e) { - throw new RuntimeException(e); - } - - } -} From 23055c1e3d10a7d54c623839ea3f961ec4506cbb Mon Sep 17 00:00:00 2001 From: MEFRREEX Date: Wed, 8 Jan 2025 20:12:22 +0200 Subject: [PATCH 12/13] fix: added method SecurityExclude#ofClass --- .../api/script/security/exclude/SecurityExclude.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/api/src/main/java/com/instancify/scriptify/api/script/security/exclude/SecurityExclude.java b/api/src/main/java/com/instancify/scriptify/api/script/security/exclude/SecurityExclude.java index 501b35d..1b14e79 100644 --- a/api/src/main/java/com/instancify/scriptify/api/script/security/exclude/SecurityExclude.java +++ b/api/src/main/java/com/instancify/scriptify/api/script/security/exclude/SecurityExclude.java @@ -21,14 +21,14 @@ default boolean isExcluded(String value) { } /** - * Creates a new exclusion instance for the package. + * Creates a new exclusion instance for the class. * - * @param value A package that will be excluded - * @return A new exclusion instance for the package + * @param value A class that will be excluded + * @return A new exclusion instance for the class */ - /*static ClassSecurityExclude ofPackage(String value) { + static ClassSecurityExclude ofClass(Class value) { return new ClassSecurityExclude(value); - }*/ + } /** * Creates a new exclusion instance for the path. From 177a0055ec925141ab2837779e0316dbad83a2a6 Mon Sep 17 00:00:00 2001 From: MEFRREEX Date: Wed, 8 Jan 2025 20:20:21 +0200 Subject: [PATCH 13/13] docs(readme): update version in readme --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 30cd5de..5cb60ac 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ For adding a library only: com.instancify.scriptify core - 1.3.0-SNAPSHOT + 1.3.1-SNAPSHOT ``` @@ -26,12 +26,12 @@ For adding a library with JS for Rhino or GraalVM: com.instancify.scriptify script-js-rhino - 1.3.0-SNAPSHOT + 1.3.1-SNAPSHOT com.instancify.scriptify script-js-graalvm - 1.3.0-SNAPSHOT + 1.3.1-SNAPSHOT ``` ## Gradle @@ -45,11 +45,11 @@ maven { For adding a library only: ```groovy -implementation "com.instancify.scriptify:core:1.3.0-SNAPSHOT" +implementation "com.instancify.scriptify:core:1.3.1-SNAPSHOT" ``` For adding a library with JS for Rhino or GraalVM: ```groovy -implementation "com.instancify.scriptify:script-js-rhino:1.3.0-SNAPSHOT" -implementation "com.instancify.scriptify:script-js-graalvm:1.3.0-SNAPSHOT" +implementation "com.instancify.scriptify:script-js-rhino:1.3.1-SNAPSHOT" +implementation "com.instancify.scriptify:script-js-graalvm:1.3.1-SNAPSHOT" ``` \ No newline at end of file