[Init] Initial commit - NetMesh terminal manager
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
This commit is contained in:
162
electron/plugins/secretLease.cjs
Normal file
162
electron/plugins/secretLease.cjs
Normal file
@@ -0,0 +1,162 @@
|
||||
"use strict";
|
||||
|
||||
const { randomBytes } = require("node:crypto");
|
||||
|
||||
const { PluginRpcError, RPC_ERRORS } = require("./rpcRouter.cjs");
|
||||
|
||||
const DEFAULT_SECRET_LEASE_TTL_MS = 30_000;
|
||||
const MAX_SECRET_LEASE_TTL_MS = 60_000;
|
||||
const MAX_SECRET_LEASES_PER_RUNTIME = 128;
|
||||
|
||||
function assertLeaseReference(lease) {
|
||||
if (
|
||||
!lease
|
||||
|| typeof lease !== "object"
|
||||
|| Array.isArray(lease)
|
||||
|| lease.kind !== "secret-lease"
|
||||
|| typeof lease.id !== "string"
|
||||
|| lease.id.length < 32
|
||||
|| lease.id.length > 256
|
||||
) throw new PluginRpcError(RPC_ERRORS.invalidArgument, "Secret lease reference is invalid");
|
||||
return lease.id;
|
||||
}
|
||||
|
||||
class SecretLeaseStore {
|
||||
constructor(options) {
|
||||
this.secretStore = options.secretStore;
|
||||
this.clock = options.clock ?? (() => Date.now());
|
||||
this.randomBytes = options.randomBytes ?? randomBytes;
|
||||
this.setTimer = options.setTimeout ?? setTimeout;
|
||||
this.clearTimer = options.clearTimeout ?? clearTimeout;
|
||||
this.leases = new Map();
|
||||
this.closed = false;
|
||||
}
|
||||
|
||||
issue(options) {
|
||||
if (this.closed) throw new PluginRpcError(RPC_ERRORS.unavailable, "Secret lease broker is closed");
|
||||
if (options.signal?.aborted) {
|
||||
throw new PluginRpcError(RPC_ERRORS.cancelled, "Secret lease operation was cancelled");
|
||||
}
|
||||
const activeCount = [...this.leases.values()].filter((lease) => (
|
||||
lease.runtimeId === options.runtimeId
|
||||
)).length;
|
||||
if (activeCount >= MAX_SECRET_LEASES_PER_RUNTIME) {
|
||||
throw new PluginRpcError(RPC_ERRORS.resourceExhausted, "Too many active secret leases");
|
||||
}
|
||||
const ttlMs = options.ttlMs ?? DEFAULT_SECRET_LEASE_TTL_MS;
|
||||
if (!Number.isSafeInteger(ttlMs) || ttlMs < 1 || ttlMs > MAX_SECRET_LEASE_TTL_MS) {
|
||||
throw new PluginRpcError(RPC_ERRORS.invalidArgument, "Secret lease lifetime is invalid");
|
||||
}
|
||||
const id = this.randomBytes(24).toString("base64url");
|
||||
if (options.resolveSecret !== undefined && typeof options.resolveSecret !== "function") {
|
||||
throw new TypeError("Secret lease resolver must be a function");
|
||||
}
|
||||
const credential = options.credential ?? options.secret;
|
||||
if (!credential) throw new TypeError("Secret lease credential is required");
|
||||
const expiresAt = this.clock() + ttlMs;
|
||||
const record = {
|
||||
id,
|
||||
pluginId: options.pluginId,
|
||||
runtimeId: options.runtimeId,
|
||||
credential,
|
||||
resolveSecret: options.resolveSecret ?? null,
|
||||
operationId: options.operationId,
|
||||
purpose: options.purpose,
|
||||
expiresAt,
|
||||
timer: null,
|
||||
abortCleanup: null,
|
||||
};
|
||||
record.timer = this.setTimer(() => this.#delete(id), ttlMs);
|
||||
record.timer?.unref?.();
|
||||
if (options.signal) {
|
||||
const onAbort = () => this.#delete(id);
|
||||
options.signal.addEventListener("abort", onAbort, { once: true });
|
||||
record.abortCleanup = () => options.signal.removeEventListener("abort", onAbort);
|
||||
}
|
||||
this.leases.set(id, record);
|
||||
if (options.signal?.aborted) {
|
||||
this.#delete(id);
|
||||
throw new PluginRpcError(RPC_ERRORS.cancelled, "Secret lease operation was cancelled");
|
||||
}
|
||||
return Object.freeze({
|
||||
kind: "secret-lease",
|
||||
id,
|
||||
operationId: options.operationId,
|
||||
expiresAt,
|
||||
});
|
||||
}
|
||||
|
||||
consume(options) {
|
||||
if (options.signal?.aborted) {
|
||||
throw new PluginRpcError(RPC_ERRORS.cancelled, "Secret lease operation was cancelled");
|
||||
}
|
||||
const id = assertLeaseReference(options.lease);
|
||||
const record = this.leases.get(id);
|
||||
if (!record || record.expiresAt <= this.clock()) {
|
||||
this.#delete(id);
|
||||
throw new PluginRpcError(RPC_ERRORS.notFound, "Secret lease is missing or expired");
|
||||
}
|
||||
if (
|
||||
record.pluginId !== options.pluginId
|
||||
|| record.runtimeId !== options.runtimeId
|
||||
|| record.operationId !== options.operationId
|
||||
) throw new PluginRpcError(RPC_ERRORS.permissionDenied, "Secret lease scope does not match the operation");
|
||||
this.#delete(id);
|
||||
const consumeContext = Object.freeze({
|
||||
pluginId: record.pluginId,
|
||||
runtimeId: record.runtimeId,
|
||||
operationId: record.operationId,
|
||||
credential: record.credential,
|
||||
signal: options.signal,
|
||||
});
|
||||
const resolved = record.resolveSecret
|
||||
? record.resolveSecret(consumeContext)
|
||||
: this.secretStore.resolve(record.pluginId, record.credential);
|
||||
if (!resolved || typeof resolved.then !== "function") {
|
||||
if (options.signal?.aborted) {
|
||||
throw new PluginRpcError(RPC_ERRORS.cancelled, "Secret lease operation was cancelled");
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
return Promise.resolve(resolved).then((value) => {
|
||||
if (options.signal?.aborted) {
|
||||
throw new PluginRpcError(RPC_ERRORS.cancelled, "Secret lease operation was cancelled");
|
||||
}
|
||||
return value;
|
||||
});
|
||||
}
|
||||
|
||||
#delete(id) {
|
||||
const record = this.leases.get(id);
|
||||
if (!record) return false;
|
||||
this.leases.delete(id);
|
||||
this.clearTimer(record.timer);
|
||||
record.abortCleanup?.();
|
||||
return true;
|
||||
}
|
||||
|
||||
revokeRuntime(runtimeId) {
|
||||
for (const [id, lease] of [...this.leases]) {
|
||||
if (lease.runtimeId === runtimeId) this.#delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
revokeOperation(pluginId, operationId) {
|
||||
for (const [id, lease] of [...this.leases]) {
|
||||
if (lease.pluginId === pluginId && lease.operationId === operationId) this.#delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
shutdown() {
|
||||
this.closed = true;
|
||||
for (const id of [...this.leases.keys()]) this.#delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
DEFAULT_SECRET_LEASE_TTL_MS,
|
||||
MAX_SECRET_LEASES_PER_RUNTIME,
|
||||
MAX_SECRET_LEASE_TTL_MS,
|
||||
SecretLeaseStore,
|
||||
assertLeaseReference,
|
||||
};
|
||||
Reference in New Issue
Block a user