From 79d2a8897abddbfc4bb386fd23f17165e2cc798d Mon Sep 17 00:00:00 2001 From: MEFRREEX Date: Sat, 16 Aug 2025 00:51:22 +0400 Subject: [PATCH 1/4] feat: move security to separate module --- script-js-graalvm/build.gradle.kts | 2 +- .../com/instancify/scriptify/script/JsScript.java | 2 +- script-js-rhino/build.gradle.kts | 2 +- .../com/instancify/scriptify/script/JsScript.java | 2 +- security/build.gradle.kts | 11 +++++++++++ .../scriptify}/security/SecurityPathAccessorImpl.java | 2 +- .../scriptify}/security/StandardSecurityManager.java | 2 +- settings.gradle.kts | 1 + 8 files changed, 18 insertions(+), 6 deletions(-) create mode 100644 security/build.gradle.kts rename {core/src/main/java/com/instancify/scriptify/core/script => security/src/main/java/com/instancify/scriptify}/security/SecurityPathAccessorImpl.java (98%) rename {core/src/main/java/com/instancify/scriptify/core/script => security/src/main/java/com/instancify/scriptify}/security/StandardSecurityManager.java (95%) diff --git a/script-js-graalvm/build.gradle.kts b/script-js-graalvm/build.gradle.kts index 6c13dec..05daa86 100644 --- a/script-js-graalvm/build.gradle.kts +++ b/script-js-graalvm/build.gradle.kts @@ -7,7 +7,7 @@ repositories { } dependencies { - api(project(":core")) + api(project(":security")) api("org.graalvm.polyglot:polyglot:24.1.1") api("org.graalvm.polyglot:js:24.1.1") } \ No newline at end of file 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 45e96c1..a63beb6 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 @@ -8,7 +8,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 com.instancify.scriptify.security.StandardSecurityManager; import org.graalvm.polyglot.Context; import org.graalvm.polyglot.HostAccess; import org.graalvm.polyglot.Value; diff --git a/script-js-rhino/build.gradle.kts b/script-js-rhino/build.gradle.kts index 0e1519b..3a03bcf 100644 --- a/script-js-rhino/build.gradle.kts +++ b/script-js-rhino/build.gradle.kts @@ -7,6 +7,6 @@ repositories { } dependencies { - api(project(":core")) + api(project(":security")) api("org.mozilla:rhino:1.8.0") } \ No newline at end of file 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 d8dffaa..a9943ea 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,7 +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 com.instancify.scriptify.security.StandardSecurityManager; import org.mozilla.javascript.Context; import org.mozilla.javascript.ScriptableObject; diff --git a/security/build.gradle.kts b/security/build.gradle.kts new file mode 100644 index 0000000..70f5c43 --- /dev/null +++ b/security/build.gradle.kts @@ -0,0 +1,11 @@ +plugins { + id("java") +} + +repositories { + mavenCentral() +} + +dependencies { + api(project(":api")) +} \ No newline at end of file diff --git a/core/src/main/java/com/instancify/scriptify/core/script/security/SecurityPathAccessorImpl.java b/security/src/main/java/com/instancify/scriptify/security/SecurityPathAccessorImpl.java similarity index 98% rename from core/src/main/java/com/instancify/scriptify/core/script/security/SecurityPathAccessorImpl.java rename to security/src/main/java/com/instancify/scriptify/security/SecurityPathAccessorImpl.java index 3fd1bfd..3fa4bfd 100644 --- a/core/src/main/java/com/instancify/scriptify/core/script/security/SecurityPathAccessorImpl.java +++ b/security/src/main/java/com/instancify/scriptify/security/SecurityPathAccessorImpl.java @@ -1,4 +1,4 @@ -package com.instancify.scriptify.core.script.security; +package com.instancify.scriptify.security; import com.instancify.scriptify.api.script.security.ScriptSecurityManager; import com.instancify.scriptify.api.script.security.SecurityPathAccessor; diff --git a/core/src/main/java/com/instancify/scriptify/core/script/security/StandardSecurityManager.java b/security/src/main/java/com/instancify/scriptify/security/StandardSecurityManager.java similarity index 95% rename from core/src/main/java/com/instancify/scriptify/core/script/security/StandardSecurityManager.java rename to security/src/main/java/com/instancify/scriptify/security/StandardSecurityManager.java index 20dbb66..39db441 100644 --- a/core/src/main/java/com/instancify/scriptify/core/script/security/StandardSecurityManager.java +++ b/security/src/main/java/com/instancify/scriptify/security/StandardSecurityManager.java @@ -1,4 +1,4 @@ -package com.instancify.scriptify.core.script.security; +package com.instancify.scriptify.security; import com.instancify.scriptify.api.script.security.ScriptSecurityManager; import com.instancify.scriptify.api.script.security.SecurityPathAccessor; diff --git a/settings.gradle.kts b/settings.gradle.kts index 1c1358b..df8de18 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,6 +1,7 @@ rootProject.name = "instancify-Scriptify" include("api") include("core") +include("security") include("script-js-graalvm") include("script-js-rhino") include("http") From 548867c90579945fc60ebc2cefeed7dbe93bd334 Mon Sep 17 00:00:00 2001 From: MEFRREEX Date: Sat, 16 Aug 2025 21:34:16 +0400 Subject: [PATCH 2/4] chore: update version --- README.md | 12 ++++++------ build.gradle.kts | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index f1f7498..b247186 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ For adding a library only: com.instancify.scriptify core - 1.3.6-SNAPSHOT + 1.4.0-SNAPSHOT ``` @@ -26,12 +26,12 @@ For adding a library with JS for Rhino or GraalVM: com.instancify.scriptify script-js-rhino - 1.3.6-SNAPSHOT + 1.4.0-SNAPSHOT com.instancify.scriptify script-js-graalvm - 1.3.6-SNAPSHOT + 1.4.0-SNAPSHOT ``` ## Gradle @@ -45,11 +45,11 @@ maven { For adding a library only: ```groovy -implementation "com.instancify.scriptify:core:1.3.6-SNAPSHOT" +implementation "com.instancify.scriptify:core:1.4.0-SNAPSHOT" ``` For adding a library with JS for Rhino or GraalVM: ```groovy -implementation "com.instancify.scriptify:script-js-rhino:1.3.6-SNAPSHOT" -implementation "com.instancify.scriptify:script-js-graalvm:1.3.6-SNAPSHOT" +implementation "com.instancify.scriptify:script-js-rhino:1.4.0-SNAPSHOT" +implementation "com.instancify.scriptify:script-js-graalvm:1.4.0-SNAPSHOT" ``` \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 9ac26cf..f856ba6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -12,7 +12,7 @@ java { allprojects { group = "com.instancify.scriptify" - version = "1.3.7-SNAPSHOT" + version = "1.4.0-SNAPSHOT" } subprojects { From 543f6aeece48227bbc4c0476b409f753cb1bac3a Mon Sep 17 00:00:00 2001 From: MEFRREEX Date: Sat, 16 Aug 2025 23:59:10 +0400 Subject: [PATCH 3/4] fix: security path accessor --- .../security/ScriptSecurityManager.java | 7 +++ .../script/security/SecurityFileSystem.java | 34 ++++++++++++++ .../impl/file/ScriptFunctionDeleteFile.java | 4 +- .../file/ScriptFunctionDownloadFromUrl.java | 11 +++-- .../impl/file/ScriptFunctionExistsFile.java | 2 +- .../impl/file/ScriptFunctionListFiles.java | 6 +-- .../impl/file/ScriptFunctionMoveFile.java | 4 +- .../impl/file/ScriptFunctionReadFile.java | 2 +- .../impl/file/ScriptFunctionWriteFile.java | 2 +- .../security/SecurityFileSystemImpl.java | 31 +++++++++++++ .../security/SecurityPathAccessorImpl.java | 46 +++++++++++++++++-- .../security/StandardSecurityManager.java | 6 +++ 12 files changed, 135 insertions(+), 20 deletions(-) create mode 100644 api/src/main/java/com/instancify/scriptify/api/script/security/SecurityFileSystem.java create mode 100644 security/src/main/java/com/instancify/scriptify/security/SecurityFileSystemImpl.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 ed6c3bd..076fa16 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 @@ -22,6 +22,13 @@ public interface ScriptSecurityManager { */ void setSecurityMode(boolean securityMode); + /** + * Receives security file system. + * + * @return Security file system + */ + SecurityFileSystem getFileSystem(); + /** * Receives security path accessor. * diff --git a/api/src/main/java/com/instancify/scriptify/api/script/security/SecurityFileSystem.java b/api/src/main/java/com/instancify/scriptify/api/script/security/SecurityFileSystem.java new file mode 100644 index 0000000..ab79ddd --- /dev/null +++ b/api/src/main/java/com/instancify/scriptify/api/script/security/SecurityFileSystem.java @@ -0,0 +1,34 @@ +package com.instancify.scriptify.api.script.security; + +import java.io.File; +import java.nio.file.Path; + +/** + * Provides secure access to files and paths, ensuring + * all operations are restricted to the configured base path. + */ +public interface SecurityFileSystem { + + /** + * Returns a secure Path inside the base path. + * + * @param path the path string to resolve + * @return a normalized and secure Path + * @throws SecurityException if the path is outside basePath or not accessible + */ + Path getPath(String path); + + /** + * Returns a secure File inside the base path. + * + * @param path the path string to resolve + * @return a normalized and secure File + * @throws SecurityException if the path is outside basePath or not accessible + */ + File getFile(String path); + + /** + * Returns the base path for this file system. + */ + Path getBasePath(); +} diff --git a/core/src/main/java/com/instancify/scriptify/core/script/function/impl/file/ScriptFunctionDeleteFile.java b/core/src/main/java/com/instancify/scriptify/core/script/function/impl/file/ScriptFunctionDeleteFile.java index 0ded336..36bd64e 100644 --- a/core/src/main/java/com/instancify/scriptify/core/script/function/impl/file/ScriptFunctionDeleteFile.java +++ b/core/src/main/java/com/instancify/scriptify/core/script/function/impl/file/ScriptFunctionDeleteFile.java @@ -30,14 +30,14 @@ public Object invoke(Script script, ScriptFunctionArgument[] args) throws Scr } if (args.length == 1) { - return new File(filePath).delete(); + return script.getSecurityManager().getFileSystem().getFile(filePath).delete(); } if (!(args[1].getValue() instanceof Boolean recursive)) { throw new ScriptFunctionArgTypeException(Boolean.class, args[1].getType()); } - File file = new File(filePath); + File file = script.getSecurityManager().getFileSystem().getFile(filePath); if (recursive) { return deleteDirectoryRecursively(file); } else { diff --git a/core/src/main/java/com/instancify/scriptify/core/script/function/impl/file/ScriptFunctionDownloadFromUrl.java b/core/src/main/java/com/instancify/scriptify/core/script/function/impl/file/ScriptFunctionDownloadFromUrl.java index d659cf1..92a8ed9 100644 --- a/core/src/main/java/com/instancify/scriptify/core/script/function/impl/file/ScriptFunctionDownloadFromUrl.java +++ b/core/src/main/java/com/instancify/scriptify/core/script/function/impl/file/ScriptFunctionDownloadFromUrl.java @@ -11,7 +11,8 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.net.URL; +import java.net.URI; +import java.net.URISyntaxException; import java.nio.file.Files; /** @@ -37,11 +38,11 @@ public Object invoke(Script script, ScriptFunctionArgument[] args) throws Scr throw new ScriptFunctionArgTypeException(String.class, args[1].getType()); } - try (InputStream in = new URL(url).openStream()) { - File targetPath = new File(filePath); + try (InputStream in = new URI(url).toURL().openStream()) { + File targetPath = script.getSecurityManager().getFileSystem().getFile(filePath); Files.copy(in, targetPath.toPath()); - } catch (IOException e) { - e.printStackTrace(); + } catch (IOException | URISyntaxException e) { + throw new RuntimeException(e); } return null; diff --git a/core/src/main/java/com/instancify/scriptify/core/script/function/impl/file/ScriptFunctionExistsFile.java b/core/src/main/java/com/instancify/scriptify/core/script/function/impl/file/ScriptFunctionExistsFile.java index 41f5726..4d81a93 100644 --- a/core/src/main/java/com/instancify/scriptify/core/script/function/impl/file/ScriptFunctionExistsFile.java +++ b/core/src/main/java/com/instancify/scriptify/core/script/function/impl/file/ScriptFunctionExistsFile.java @@ -25,7 +25,7 @@ public class ScriptFunctionExistsFile implements ScriptFunction { public Object invoke(Script script, ScriptFunctionArgument[] args) throws ScriptFunctionException { if (args.length == 1) { if (args[0].getValue() instanceof String filePath) { - return Files.exists(Path.of(filePath)); + return Files.exists(script.getSecurityManager().getFileSystem().getPath(filePath)); } else { throw new ScriptFunctionArgTypeException(String.class, args[0].getType()); } diff --git a/core/src/main/java/com/instancify/scriptify/core/script/function/impl/file/ScriptFunctionListFiles.java b/core/src/main/java/com/instancify/scriptify/core/script/function/impl/file/ScriptFunctionListFiles.java index 14ceb6e..bc0477c 100644 --- a/core/src/main/java/com/instancify/scriptify/core/script/function/impl/file/ScriptFunctionListFiles.java +++ b/core/src/main/java/com/instancify/scriptify/core/script/function/impl/file/ScriptFunctionListFiles.java @@ -9,8 +9,8 @@ import org.jetbrains.annotations.NotNull; import java.io.File; -import java.nio.file.Paths; import java.util.Arrays; +import java.util.Objects; /** * Represents a function to get all files in a folder @@ -26,9 +26,9 @@ public class ScriptFunctionListFiles implements ScriptFunction { public Object invoke(Script script, ScriptFunctionArgument[] args) throws ScriptFunctionException { if (args.length == 1) { if (args[0].getValue() instanceof String filePath) { - File folder = Paths.get(filePath).toAbsolutePath().toFile(); + File folder = script.getSecurityManager().getFileSystem().getFile(filePath); if (folder.isDirectory()) { - return Arrays.stream(folder.listFiles()).map(File::getAbsolutePath).toList(); + return Arrays.stream(Objects.requireNonNull(folder.listFiles())).map(File::getAbsolutePath).toList(); } else { throw new ScriptFunctionException("File is not a folder"); } diff --git a/core/src/main/java/com/instancify/scriptify/core/script/function/impl/file/ScriptFunctionMoveFile.java b/core/src/main/java/com/instancify/scriptify/core/script/function/impl/file/ScriptFunctionMoveFile.java index 3174c48..3a4dc36 100644 --- a/core/src/main/java/com/instancify/scriptify/core/script/function/impl/file/ScriptFunctionMoveFile.java +++ b/core/src/main/java/com/instancify/scriptify/core/script/function/impl/file/ScriptFunctionMoveFile.java @@ -33,7 +33,7 @@ public Object invoke(Script script, ScriptFunctionArgument[] args) throws Scr throw new ScriptFunctionArgTypeException(String.class, args[1].getType()); } - File fileToMove = new File(originalFilePath); - return fileToMove.renameTo(new File(targetFilePath)); + File fileToMove = script.getSecurityManager().getFileSystem().getFile(originalFilePath); + return fileToMove.renameTo(script.getSecurityManager().getFileSystem().getFile(targetFilePath)); } } diff --git a/core/src/main/java/com/instancify/scriptify/core/script/function/impl/file/ScriptFunctionReadFile.java b/core/src/main/java/com/instancify/scriptify/core/script/function/impl/file/ScriptFunctionReadFile.java index 7bc6261..abcaa45 100644 --- a/core/src/main/java/com/instancify/scriptify/core/script/function/impl/file/ScriptFunctionReadFile.java +++ b/core/src/main/java/com/instancify/scriptify/core/script/function/impl/file/ScriptFunctionReadFile.java @@ -27,7 +27,7 @@ public Object invoke(Script script, ScriptFunctionArgument[] args) throws Scr if (args.length == 1) { if (args[0].getValue() instanceof String filePath) { try { - return Files.readString(Path.of(filePath)); + return Files.readString(script.getSecurityManager().getFileSystem().getPath(filePath)); } catch (IOException e) { throw new ScriptFunctionException(e); } 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 8b84678..f2f2b60 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 @@ -26,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().getPathAccessor().getAccessiblePath(filePath), fileContent); + return Files.writeString(script.getSecurityManager().getFileSystem().getPath(filePath), fileContent); } catch (IOException e) { throw new ScriptFunctionException(e); } diff --git a/security/src/main/java/com/instancify/scriptify/security/SecurityFileSystemImpl.java b/security/src/main/java/com/instancify/scriptify/security/SecurityFileSystemImpl.java new file mode 100644 index 0000000..fe963b1 --- /dev/null +++ b/security/src/main/java/com/instancify/scriptify/security/SecurityFileSystemImpl.java @@ -0,0 +1,31 @@ +package com.instancify.scriptify.security; + +import com.instancify.scriptify.api.script.security.SecurityFileSystem; +import com.instancify.scriptify.api.script.security.SecurityPathAccessor; + +import java.io.File; +import java.nio.file.Path; + +public class SecurityFileSystemImpl implements SecurityFileSystem { + + private final SecurityPathAccessor pathAccessor; + + public SecurityFileSystemImpl(SecurityPathAccessor pathAccessor) { + this.pathAccessor = pathAccessor; + } + + @Override + public Path getPath(String path) { + return pathAccessor.getAccessiblePath(path); + } + + @Override + public File getFile(String path) { + return this.getPath(path).toFile(); + } + + @Override + public Path getBasePath() { + return pathAccessor.getBasePath(); + } +} diff --git a/security/src/main/java/com/instancify/scriptify/security/SecurityPathAccessorImpl.java b/security/src/main/java/com/instancify/scriptify/security/SecurityPathAccessorImpl.java index 3fa4bfd..3aea9d8 100644 --- a/security/src/main/java/com/instancify/scriptify/security/SecurityPathAccessorImpl.java +++ b/security/src/main/java/com/instancify/scriptify/security/SecurityPathAccessorImpl.java @@ -57,18 +57,31 @@ public void setBasePath(Path 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. + * Returns a path that is safe to access according to security rules. + * If the path is accessible via exclusions, returns the normalized path. + * If the path is not accessible, creates a safe path within basePath by cleaning the path from invalid characters. * * @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 + * @return A Path object representing the accessible path or a path within base directory */ @Override public Path getAccessiblePath(String path) { if (this.isAccessible(path)) { - return Path.of(path); + // Path is in exclusions - return it normalized + return Paths.get(path).normalize().toAbsolutePath(); } - return Path.of(basePath.toString(), path.replaceAll(":", "")); + + // Path is not accessible - create safe path within basePath + // We need to manually combine paths because resolve() ignores basePath for absolute paths + Path safePath = Paths.get(basePath.toString(), path.replace(":", "")).normalize(); + + // CRITICAL: Ensure the result stays within basePath boundaries + if (!safePath.startsWith(basePath)) { + // If path tries to escape basePath (e.g., "../"), return basePath itself + return basePath; + } + + return safePath; } /** @@ -83,12 +96,35 @@ public boolean isAccessible(String path) { return true; } + // Normalize the path to resolve .. and . components to prevent path traversal + Path normalizedPath; + try { + normalizedPath = Paths.get(path).normalize().toAbsolutePath(); + } catch (Exception e) { + return false; + } + + // Check both original and normalized path against exclusions for compatibility + String normalizedPathString = normalizedPath.toString(); + // Search all exclusions and check that the path is excluded for (SecurityExclude exclude : securityManager.getExcludes()) { if (exclude instanceof PathSecurityExclude) { + // Check original path first if (exclude.isExcluded(path)) { return true; } + + // Check normalized path + if (exclude.isExcluded(normalizedPathString)) { + return true; + } + + // Check with forward slashes for cross-platform compatibility + String pathWithForwardSlashes = normalizedPathString.replace('\\', '/'); + if (exclude.isExcluded(pathWithForwardSlashes)) { + return true; + } } } diff --git a/security/src/main/java/com/instancify/scriptify/security/StandardSecurityManager.java b/security/src/main/java/com/instancify/scriptify/security/StandardSecurityManager.java index 39db441..cf5e971 100644 --- a/security/src/main/java/com/instancify/scriptify/security/StandardSecurityManager.java +++ b/security/src/main/java/com/instancify/scriptify/security/StandardSecurityManager.java @@ -12,6 +12,7 @@ public class StandardSecurityManager implements ScriptSecurityManager { private boolean securityMode; private final Set excludes = new HashSet<>(); private final SecurityPathAccessor pathAccessor = new SecurityPathAccessorImpl(this); + private final SecurityFileSystemImpl fileSystem = new SecurityFileSystemImpl(pathAccessor); @Override public boolean getSecurityMode() { @@ -23,6 +24,11 @@ public void setSecurityMode(boolean securityMode) { this.securityMode = securityMode; } + @Override + public SecurityFileSystemImpl getFileSystem() { + return fileSystem; + } + @Override public SecurityPathAccessor getPathAccessor() { return pathAccessor; From 1d9d6c56832bbd22db67ee1addf23f3e7550de77 Mon Sep 17 00:00:00 2001 From: MEFRREEX Date: Sat, 16 Aug 2025 23:59:31 +0400 Subject: [PATCH 4/4] chore: update readme --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index b247186..b827f3f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,10 @@ # Instancify Scriptify Instancify Scriptify is a library to evaluate scripts written in JavaScript with ability to add global functions and variables. +## What is it for? +This library is designed to execute JavaScript scripts and has the ability to register global functions and constants. +It also allows you to configure security for executing scripts. + ## Maven Adding repo: ```xml