Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,9 @@ dependencies {
implementation 'ch.digitalfondue.vatchecker:vatchecker:1.6.0'
implementation 'ch.digitalfondue.basicxlsx:basicxlsx:0.7.1'
implementation 'org.imgscalr:imgscalr-lib:4.2'
implementation 'org.mozilla:rhino-runtime:1.7.15.1'
implementation 'io.roastedroot:quickjs4j:0.0.17'
implementation 'io.roastedroot:quickjs4j-annotations:0.0.17'
annotationProcessor 'io.roastedroot:quickjs4j-processor:0.0.17'
implementation 'com.google.auth:google-auth-library-oauth2-http:1.41.0'

testImplementation 'org.testcontainers:testcontainers'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,25 @@
* You should have received a copy of the GNU General Public License
* along with alf.io. If not, see <http://www.gnu.org/licenses/>.
*/
package alfio.extension.exception;
package alfio.extension;

/**
* Exception thrown if some out of boundaries usage (forbidden methods calls) happens when executing a script.
*/
public class OutOfBoundariesException extends AlfioScriptingException {
import io.roastedroot.quickjs4j.annotations.Builtins;
import io.roastedroot.quickjs4j.annotations.HostFunction;

public OutOfBoundariesException(String message) {
super(message);
}
@Builtins("console")
class ConsoleApi {
private final ConsoleLogger logger;

public OutOfBoundariesException(String message, Throwable cause) {
super(message, cause);
ConsoleApi(ConsoleLogger logger) {
this.logger = logger;
}

@HostFunction
public void log(String msg) { logger.log(msg); }

@HostFunction
public void warn(String msg) { logger.warn(msg); }

@HostFunction
public void error(String msg) { logger.error(msg); }
}
1 change: 0 additions & 1 deletion src/main/java/alfio/extension/ConsoleLogger.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ private static String composeMessage(Object first, Object... others) {
if(others != null) {
parameterPlaceholders = " %s".repeat(others.length);
paramObjects = Stream.concat(Stream.of(first), Arrays.stream(others))
.map(ExtensionUtils::unwrap)
.toArray();
} else {
paramObjects = new Object[] {first};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,28 @@
* You should have received a copy of the GNU General Public License
* along with alf.io. If not, see <http://www.gnu.org/licenses/>.
*/
package alfio.extension.support;
package alfio.extension;

import alfio.extension.exception.OutOfBoundariesException;
import org.mozilla.javascript.NativeJavaList;
import org.mozilla.javascript.Scriptable;
import io.roastedroot.quickjs4j.annotations.Builtins;
import io.roastedroot.quickjs4j.annotations.HostFunction;

public class SandboxNativeJavaList extends NativeJavaList {
public SandboxNativeJavaList(Scriptable scope, Object map) {
super(scope, map);
@Builtins("extensionLogger")
class ExtensionLoggerApi {
private final ExtensionLogger logger;

ExtensionLoggerApi(ExtensionLogger logger) {
this.logger = logger;
}

@Override
public Object get(String name, Scriptable start) {
if (name.equals("getClass")) {
throw new OutOfBoundariesException("Out of boundaries class use.");
}
@HostFunction
public void logInfo(String msg) { logger.logInfo(msg); }

return super.get(name, start);
}
@HostFunction
public void logWarning(String msg) { logger.logWarning(msg); }

@HostFunction
public void logError(String msg) { logger.logError(msg); }

@HostFunction
public void logSuccess(String msg) { logger.logSuccess(msg); }
}
10 changes: 4 additions & 6 deletions src/main/java/alfio/extension/ExtensionService.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@
@AllArgsConstructor
public class ExtensionService {

private static final String EVALUATE_RESULT = "res = GSON.fromJson(JSON.stringify(res), returnClass);";
private static final String PROCESS_EXTENSION_RESULT = "var res = executeScript(extensionEvent); " + EVALUATE_RESULT;
private static final String PROCESS_CAPABILITY_RESULT = "var res = executeCapability(capability); " + EVALUATE_RESULT;
private static final String SET_RESULT = "alfio_internal.setResult(JSON.stringify(res));";
private static final String PROCESS_EXTENSION_RESULT = "var res = executeScript(extensionEvent); " + SET_RESULT;
private static final String PROCESS_CAPABILITY_RESULT = "var res = executeCapability(capability); " + SET_RESULT;
private static final String EXECUTE_SCRIPT = "executeScript(extensionEvent);";
private static final String OUTPUT = "output";
private static final String EXECUTION_KEY = "executionKey";
Expand Down Expand Up @@ -112,7 +112,7 @@ private static final class NoopExtensionLogger implements ExtensionLogger {
private ExtensionMetadata getMetadata(String name, String script) {
return scriptingExecutionService.executeScript(
name,
script + "\n;GSON.fromJson(JSON.stringify(getScriptMetadata()), returnClass);", //<- ugly hack, but the interop java<->js is simpler that way...
script + "\n;alfio_internal.setResult(JSON.stringify(getScriptMetadata()));",
Collections.emptyMap(),
ExtensionMetadata.class, new NoopExtensionLogger());
}
Expand All @@ -121,8 +121,6 @@ private ExtensionMetadata getMetadata(String name, String script) {
public void createOrUpdate(String previousPath, String previousName, Extension script) {
Validate.notBlank(script.getName(), "Name is mandatory");
Validate.notBlank(script.getPath(), "Path must be defined");
ScriptValidation validation = new ScriptValidation(script.getScript());
validation.validate();
String hash = DigestUtils.sha256Hex(script.getScript());
ExtensionMetadata extensionMetadata = getMetadata(script.getName(), script.getScript());

Expand Down
54 changes: 3 additions & 51 deletions src/main/java/alfio/extension/ExtensionUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import org.apache.commons.codec.digest.HmacAlgorithms;
import org.apache.commons.codec.digest.HmacUtils;
import org.apache.commons.lang3.StringUtils;
import org.mozilla.javascript.*;

import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
Expand All @@ -31,7 +30,8 @@
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.Arrays;
import java.util.Base64;
import java.util.stream.Collectors;

public class ExtensionUtils {
Expand Down Expand Up @@ -83,56 +83,8 @@ public static String base64UrlSafe(String input) {
.encodeToString(input.getBytes(StandardCharsets.UTF_8));
}

/**
* Unwrap / convert all Scriptable object to their java native counterpart and transform to json
* <ul>
* <li>NativeArray -> ArrayList</li>
* <li>NativeJavaObject -> unwrapped java object</li>
* <li>NativeObject -> LinkedHashMap</li>
* </ul>
*
* @param o
* @return
*/
public static String convertToJson(Object o) {
return JSON_SERIALIZER.toJson(unwrap(o));
}

static Object unwrap(Object o) {
if (o instanceof Scriptable) {
if (o instanceof NativeArray na) {
List<Object> res = new ArrayList<>();
for (var a : na) {
res.add(unwrap(a));
}
return res;
} else if (o instanceof NativeJavaObject object) {
return object.unwrap();
} else if (o instanceof NativeObject na) {
Map<Object, Object> res = new LinkedHashMap<>();
for (var kv : na.entrySet()) {
res.put(kv.getKey(), unwrap(kv.getValue()));
}
return res;
} else if (o instanceof IdScriptableObject object) {
return parseIdScriptableObject(object);
}
} else if (o instanceof CharSequence) {
return o.toString();
}
return o;
}

private static Object parseIdScriptableObject(IdScriptableObject object) {
var className = object.getClassName();
return switch (className) {
case "String" -> ScriptRuntime.toCharSequence(object);
case "Boolean" -> Context.jsToJava(object, Boolean.class);
case "Date" -> Context.jsToJava(object, Date.class);
default ->
// better safe than sorry: we ignore all the unknown objects
null;
};
return JSON_SERIALIZER.toJson(o);
}

/**
Expand Down
60 changes: 60 additions & 0 deletions src/main/java/alfio/extension/ExtensionUtilsApi.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/**
* This file is part of alf.io.
*
* alf.io is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* alf.io is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with alf.io. If not, see <http://www.gnu.org/licenses/>.
*/
package alfio.extension;

import io.roastedroot.quickjs4j.annotations.Builtins;
import io.roastedroot.quickjs4j.annotations.HostFunction;

import java.util.List;

@Builtins("ExtensionUtils")
@SuppressWarnings({"rawtypes", "unchecked"})
class ExtensionUtilsApi {

@HostFunction
public String md5(String str) {
return ExtensionUtils.md5(str);
}

@HostFunction("base64UrlSafe")
public String base64UrlSafe(String str) {
return ExtensionUtils.base64UrlSafe(str);
}

@HostFunction
public String convertToJson(String str) {
// identity — JS already stringified the object before calling
return str;
}

@HostFunction
public String format(String str, List params) {
String[] arr = params != null ? (String[]) params.toArray(new String[0]) : new String[0];
return ExtensionUtils.format(str, arr);
}

@HostFunction
public String computeHMAC(String secret, List parts) {
String[] arr = parts != null ? (String[]) parts.toArray(new String[0]) : new String[0];
return ExtensionUtils.computeHMAC(secret, arr);
}

@HostFunction
public String formatDateTime(String dateTime, String pattern, boolean utc) {
return ExtensionUtils.formatDateTime(dateTime, pattern, utc);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,21 @@
* You should have received a copy of the GNU General Public License
* along with alf.io. If not, see <http://www.gnu.org/licenses/>.
*/
package alfio.extension.exception;
package alfio.extension;

/**
* Exception thrown if script is not valid for execution.
*/
public class ScriptNotValidException extends AlfioScriptingException {
public ScriptNotValidException(String message) {
super(message);
import io.roastedroot.quickjs4j.annotations.Builtins;
import io.roastedroot.quickjs4j.annotations.HostFunction;

@Builtins("alfio_internal")
class InternalApi {
private String resultJson;

@HostFunction
public void setResult(String json) {
this.resultJson = json;
}

String getResultJson() {
return resultJson;
}
}
51 changes: 0 additions & 51 deletions src/main/java/alfio/extension/JSErrorReporter.java

This file was deleted.

Loading
Loading