From 7283f38aa3eb3037f85cdf14173daed78c376cf4 Mon Sep 17 00:00:00 2001 From: crschnick Date: Wed, 19 Aug 2026 18:16:18 +0000 Subject: [PATCH] Rework --- .../app/beacon/BeaconRequestHandler.java | 15 +++-- .../xpipe/app/beacon/api/FsReadExchange.java | 6 +- .../app/beacon/api/FsScriptExchange.java | 1 - .../xpipe/app/beacon/api/FsWriteExchange.java | 10 ++++ .../xpipe/app/secret/InPlaceSecretValue.java | 2 - .../io/xpipe/app/util/AppJacksonModule.java | 56 +++++++++++++++++++ .../io/xpipe/ext/base/script/ScriptStore.java | 2 - .../ext/base/script/ScriptStoreProvider.java | 1 - lang/strings/translations_en.properties | 3 +- 9 files changed, 83 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/io/xpipe/app/beacon/BeaconRequestHandler.java b/app/src/main/java/io/xpipe/app/beacon/BeaconRequestHandler.java index b7cb87c90..dc4dcc2f4 100644 --- a/app/src/main/java/io/xpipe/app/beacon/BeaconRequestHandler.java +++ b/app/src/main/java/io/xpipe/app/beacon/BeaconRequestHandler.java @@ -106,13 +106,18 @@ public class BeaconRequestHandler implements HttpHandler { return; } - var sync = beaconInterface.getSynchronizationObject(); - if (sync != null) { - synchronized (sync) { + try { + var sync = beaconInterface.getSynchronizationObject(); + if (sync != null) { + synchronized (sync) { + response = beaconInterface.handle(exchange, object); + } + } else { response = beaconInterface.handle(exchange, object); } - } else { - response = beaconInterface.handle(exchange, object); + } catch (IOException ioe) { + // Prevent IO exception from being interpreted as beacon connection issue + throw new BeaconServerException(ioe); } } catch (BeaconClientException clientException) { ErrorEventFactory.fromThrowable(clientException).omit().expected().handle(); diff --git a/app/src/main/java/io/xpipe/app/beacon/api/FsReadExchange.java b/app/src/main/java/io/xpipe/app/beacon/api/FsReadExchange.java index d2c9cd3ab..4503387a8 100644 --- a/app/src/main/java/io/xpipe/app/beacon/api/FsReadExchange.java +++ b/app/src/main/java/io/xpipe/app/beacon/api/FsReadExchange.java @@ -33,8 +33,12 @@ public class FsReadExchange extends BeaconInterface { var shell = AppBeaconServer.get().getCache().getShellSession(msg.getStore()); var fs = new ShellFileSystem(shell.getControl()); + if (!msg.getPath().isAbsolute()) { + throw new BeaconClientException("File path " + msg.getPath() + " is not absolute"); + } + if (!fs.fileExists(msg.getPath())) { - throw new BeaconClientException("File does not exist"); + throw new BeaconClientException("File " + msg.getPath() + " does not exist"); } var size = fs.getFileSize(msg.getPath()); diff --git a/app/src/main/java/io/xpipe/app/beacon/api/FsScriptExchange.java b/app/src/main/java/io/xpipe/app/beacon/api/FsScriptExchange.java index 0f7645497..34fe30b22 100644 --- a/app/src/main/java/io/xpipe/app/beacon/api/FsScriptExchange.java +++ b/app/src/main/java/io/xpipe/app/beacon/api/FsScriptExchange.java @@ -31,7 +31,6 @@ public class FsScriptExchange extends BeaconInterface try (var in = BlobManager.get().getBlob(msg.getBlob())) { data = new String(in.readAllBytes(), StandardCharsets.UTF_8); } - data = shell.getControl().getShellDialect().prepareScriptContent(shell.getControl(), data); var file = ScriptHelper.createExecScript(shell.getControl(), data); return Response.builder().path(file).build(); } diff --git a/app/src/main/java/io/xpipe/app/beacon/api/FsWriteExchange.java b/app/src/main/java/io/xpipe/app/beacon/api/FsWriteExchange.java index a1294743e..5a342dc34 100644 --- a/app/src/main/java/io/xpipe/app/beacon/api/FsWriteExchange.java +++ b/app/src/main/java/io/xpipe/app/beacon/api/FsWriteExchange.java @@ -1,6 +1,7 @@ package io.xpipe.app.beacon.api; import io.xpipe.app.beacon.AppBeaconServer; +import io.xpipe.app.beacon.BeaconClientException; import io.xpipe.app.beacon.BeaconInterface; import io.xpipe.app.beacon.BlobManager; import io.xpipe.app.fs.ShellFileSystem; @@ -27,6 +28,15 @@ public class FsWriteExchange extends BeaconInterface { public Object handle(HttpExchange exchange, Request msg) { var shell = AppBeaconServer.get().getCache().getShellSession(msg.getStore()); var fs = new ShellFileSystem(shell.getControl()); + + if (!msg.getPath().isAbsolute()) { + throw new BeaconClientException("File path " + msg.getPath() + " is not absolute"); + } + + if (!fs.directoryExists(msg.getPath().getParent())) { + throw new BeaconClientException("Directory " + msg.getPath().getParent() + " does not exist"); + } + try (var in = BlobManager.get().getBlob(msg.getBlob()); var os = fs.openOutput(msg.getPath(), BlobManager.get().getSize(msg.getBlob()))) { in.transferTo(os); diff --git a/app/src/main/java/io/xpipe/app/secret/InPlaceSecretValue.java b/app/src/main/java/io/xpipe/app/secret/InPlaceSecretValue.java index 5c619b6eb..94109ad23 100644 --- a/app/src/main/java/io/xpipe/app/secret/InPlaceSecretValue.java +++ b/app/src/main/java/io/xpipe/app/secret/InPlaceSecretValue.java @@ -16,9 +16,7 @@ import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.SecretKeySpec; -@JsonTypeName("internal") @SuperBuilder -@Jacksonized @EqualsAndHashCode(callSuper = true) public class InPlaceSecretValue extends AesSecretValue { diff --git a/app/src/main/java/io/xpipe/app/util/AppJacksonModule.java b/app/src/main/java/io/xpipe/app/util/AppJacksonModule.java index b80caf89e..c9125649d 100644 --- a/app/src/main/java/io/xpipe/app/util/AppJacksonModule.java +++ b/app/src/main/java/io/xpipe/app/util/AppJacksonModule.java @@ -23,6 +23,7 @@ import tools.jackson.core.JsonParser; import tools.jackson.core.TokenStreamLocation; import tools.jackson.core.type.TypeReference; import tools.jackson.databind.*; +import tools.jackson.databind.DeserializationContext; import tools.jackson.databind.annotation.JsonSerialize; import tools.jackson.databind.jsontype.NamedType; import tools.jackson.databind.jsontype.TypeDeserializer; @@ -32,6 +33,7 @@ import tools.jackson.databind.module.SimpleModule; import tools.jackson.databind.node.JsonNodeFactory; import java.io.CharArrayReader; +import java.io.IOException; import java.nio.charset.Charset; import java.nio.file.InvalidPathException; import java.nio.file.Path; @@ -70,6 +72,9 @@ public class AppJacksonModule extends SimpleModule { registerSubtypes(ExternalSpiceClient.getClasses()); registerSubtypes(SecretRetrievalStrategy.getClasses()); + addSerializer(InPlaceSecretValue.class, new InPlaceSecretValueSerializer()); + addDeserializer(InPlaceSecretValue.class, new InPlaceSecretValueDeserializer()); + addSerializer(DataStoreEntryRef.class, new DataStoreEntryRefSerializer()); addDeserializer(DataStoreEntryRef.class, new DataStoreEntryRefDeserializer()); @@ -118,6 +123,57 @@ public class AppJacksonModule extends SimpleModule { super.setupModule(context); } + public static class InPlaceSecretValueSerializer extends ValueSerializer { + + @Override + public void serializeWithType(InPlaceSecretValue value, JsonGenerator gen, SerializationContext ctxt, TypeSerializer typeSer) throws + JacksonException { + serialize(value, gen, ctxt); + } + + @Override + public void serialize(InPlaceSecretValue value, JsonGenerator jgen, SerializationContext context) { + if (value == null) { + jgen.writeNull(); + return; + } + + var tree = JsonNodeFactory.instance.objectNode(); + tree.put("type", "internal"); + tree.put("encryptedValue", value.getEncryptedValue()); + jgen.writeTree(tree); + } + } + + public static class InPlaceSecretValueDeserializer extends ValueDeserializer { + + @Override + public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws JacksonException { + return deserialize(p, ctxt); + } + + @Override + public InPlaceSecretValue deserialize(JsonParser p, DeserializationContext ctxt) throws + JacksonException { + JsonNode tree = JacksonMapper.getDefault().readTree(p); + if (tree.isString()) { + return InPlaceSecretValue.of(tree.stringValue()); + } + + var type = tree.get("type"); + if (type == null || !type.asString().equals("internal")) { + return null; + } + + var enc = tree.get("encryptedValue"); + if (enc == null) { + return null; + } + + return InPlaceSecretValue.builder().encryptedValue(enc.stringValue()).build(); + } + } + public static class EncryptionTokenSerializer extends ValueSerializer { @Override diff --git a/ext/base/src/main/java/io/xpipe/ext/base/script/ScriptStore.java b/ext/base/src/main/java/io/xpipe/ext/base/script/ScriptStore.java index 8ffccec4c..0ccf0efb2 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/script/ScriptStore.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/script/ScriptStore.java @@ -31,8 +31,6 @@ public class ScriptStore implements SelfReferentialStore, StatefulDataStore> scripts; - String description; - ScriptTextSource textSource; boolean initScript; boolean shellScript; diff --git a/ext/base/src/main/java/io/xpipe/ext/base/script/ScriptStoreProvider.java b/ext/base/src/main/java/io/xpipe/ext/base/script/ScriptStoreProvider.java index 9ed223ea5..7e4b4bcea 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/script/ScriptStoreProvider.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/script/ScriptStoreProvider.java @@ -162,7 +162,6 @@ public class ScriptStoreProvider implements DataStoreProvider { return ScriptStore.builder() .textSource(textSource.get()) .scripts(new ArrayList<>(others.get())) - .description(st.getDescription()) .initScript(selectedExecTypes.contains(0)) .runnableScript(selectedExecTypes.contains(1)) .fileScript(selectedExecTypes.contains(2)) diff --git a/lang/strings/translations_en.properties b/lang/strings/translations_en.properties index 5cb7478e3..bb5de5fe1 100644 --- a/lang/strings/translations_en.properties +++ b/lang/strings/translations_en.properties @@ -1982,7 +1982,8 @@ keePassXcOpenSshAgent=Use OpenSSH agent keePassXcPageant=Use Pageant passwordManagerSshAgentSocket=SSH agent socket passwordManagerSshAgentSocketDescription=The socket that should be used to communicate with the agent. -passwordManagerSshKeysNotSupported=The current password manager configuration does not support retrieving SSH keys via an agent +#force +passwordManagerSshKeysNotSupported=The current password manager configuration does not support retrieving SSH keys passwordManagerAdditionalKey=Additional SSH key passwordManagerAdditionalKeyDescription=The SSH key to use in addition to the credentials from the password manager multiIdentity.displayName=Multi identity