mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-10 20:46:28 +01:00
fix(exe-dev): skip PEM validation when sshPrivateKey is a saved secret-ref UUID (PAPA-407)
When the operator picks a saved secret in the JsonSchemaForm, the persisted value is the bare UUID of the secret record, not the PEM. Run-time secret resolution swaps in the actual key before SSH, but config save-time validation was rejecting the UUID as an invalid PEM. Skip PEM checks when the value matches the server-side secret-ref UUID regex.
This commit is contained in:
1 parent
bf8a7c98ea
commit
fe9cd6bcd6
2 files changed
+26
-1
No files matched your search
@@ -202,6 +202,26 @@ describe("exe.dev sandbox provider plugin", () => {
|
||||
expect(validateSshPrivateKey(VALID_RSA_PEM)).toBeNull();
|
||||
});
|
||||
|
||||
it("accepts UUID-like secret reference values from the save-time schema stage", async () => {
|
||||
process.env.EXE_API_KEY = "host-key";
|
||||
|
||||
const result = await plugin.definition.onEnvironmentValidateConfig?.({
|
||||
driverKey: "exe-dev",
|
||||
config: {
|
||||
apiKey: "api-key",
|
||||
sshPrivateKey: "11111111-1111-4111-8111-111111111111",
|
||||
},
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: true,
|
||||
normalizedConfig: {
|
||||
sshPrivateKey: "11111111-1111-4111-8111-111111111111",
|
||||
},
|
||||
});
|
||||
expect(result?.errors ?? []).toEqual([]);
|
||||
});
|
||||
|
||||
it("treats empty / whitespace-only input as valid (falls back to on-host key)", () => {
|
||||
expect(validateSshPrivateKey("")).toBeNull();
|
||||
expect(validateSshPrivateKey(" \n\n ")).toBeNull();
|
||||
|
||||
@@ -69,6 +69,7 @@ const MAX_VM_RECORD_DEPTH = 4;
|
||||
const EXE_DEV_SSH_ONBOARDING_MARKER = "Please complete registration by running: ssh exe.dev";
|
||||
const EXE_DEV_SSH_EMAIL_PROMPT = "Please enter your email address:";
|
||||
const EXE_DEV_SSH_INVALID_KEY_FORMAT = /Load key [^\n]*invalid format/i;
|
||||
const UUID_SECRET_REF_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
||||
|
||||
// exe.dev's `--setup-script` runs at VM init as the unprivileged `exedev` user, which
|
||||
// has passwordless sudo. The Paperclip sandbox callback bridge is a Node script, so
|
||||
@@ -140,6 +141,10 @@ function isValidUrl(value: string): boolean {
|
||||
}
|
||||
}
|
||||
|
||||
function isSecretRef(value: string): boolean {
|
||||
return UUID_SECRET_REF_RE.test(value);
|
||||
}
|
||||
|
||||
// Catch the SSH-key paste failure modes we've seen in the wild (wrong file,
|
||||
// PPK export, truncated paste) before the user pays the cost of provisioning a
|
||||
// VM and getting a cryptic SSH error. Inline parse — no `ssh-keygen` dependency
|
||||
@@ -758,7 +763,7 @@ const plugin = definePlugin({
|
||||
) {
|
||||
errors.push("strictHostKeyChecking cannot be empty.");
|
||||
}
|
||||
if (config.sshPrivateKey) {
|
||||
if (config.sshPrivateKey && !isSecretRef(config.sshPrivateKey)) {
|
||||
const sshKeyError = validateSshPrivateKey(config.sshPrivateKey);
|
||||
if (sshKeyError) errors.push(sshKeyError);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user