Initialize project and ignore local references
This commit is contained in:
1 parent
a11ac3eaba
commit
2af82b6bb3
132 files changed
+29426
No files matched your search
@@ -0,0 +1,91 @@
|
||||
import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
|
||||
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { safeStorage } from "electron";
|
||||
|
||||
export class SecretVault {
|
||||
private readonly keyPath: string;
|
||||
|
||||
constructor(private readonly profileDir: string) {
|
||||
this.keyPath = join(profileDir, "vault.key");
|
||||
}
|
||||
|
||||
getBackendLabel(): string {
|
||||
if (this.canUseSafeStorage()) {
|
||||
const selectedBackend = typeof safeStorage?.getSelectedStorageBackend === "function"
|
||||
? safeStorage.getSelectedStorageBackend()
|
||||
: "platform";
|
||||
if (selectedBackend && selectedBackend !== "basic_text") {
|
||||
return `safe_storage:${selectedBackend}`;
|
||||
}
|
||||
}
|
||||
return this.allowInsecureVaultFallback() ? "vault:aes-256-gcm" : "unavailable";
|
||||
}
|
||||
|
||||
encrypt(value: string): { backend: "safe_storage" | "vault"; payload: Buffer } {
|
||||
if (this.canUseSafeStorage()) {
|
||||
const selectedBackend = typeof safeStorage?.getSelectedStorageBackend === "function"
|
||||
? safeStorage.getSelectedStorageBackend()
|
||||
: "platform";
|
||||
if (selectedBackend !== "basic_text") {
|
||||
return {
|
||||
backend: "safe_storage",
|
||||
payload: safeStorage.encryptString(value),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.allowInsecureVaultFallback()) {
|
||||
throw new Error("Secure secret storage is unavailable. AgentCompany requires OS-backed credential storage unless AGENTCOMPANY_ALLOW_INSECURE_VAULT=true is set for test-only environments.");
|
||||
}
|
||||
|
||||
const iv = randomBytes(12);
|
||||
const key = this.ensureKey();
|
||||
const cipher = createCipheriv("aes-256-gcm", key, iv);
|
||||
const encrypted = Buffer.concat([cipher.update(value, "utf8"), cipher.final()]);
|
||||
const tag = cipher.getAuthTag();
|
||||
return {
|
||||
backend: "vault",
|
||||
payload: Buffer.concat([iv, tag, encrypted]),
|
||||
};
|
||||
}
|
||||
|
||||
decrypt(backend: "safe_storage" | "vault", payload: Buffer): string {
|
||||
if (backend === "safe_storage") {
|
||||
if (!this.canUseSafeStorage()) {
|
||||
throw new Error("safeStorage backend is unavailable in the current runtime.");
|
||||
}
|
||||
return safeStorage.decryptString(payload);
|
||||
}
|
||||
const key = this.ensureKey();
|
||||
const iv = payload.subarray(0, 12);
|
||||
const tag = payload.subarray(12, 28);
|
||||
const encrypted = payload.subarray(28);
|
||||
const decipher = createDecipheriv("aes-256-gcm", key, iv);
|
||||
decipher.setAuthTag(tag);
|
||||
return Buffer.concat([decipher.update(encrypted), decipher.final()]).toString("utf8");
|
||||
}
|
||||
|
||||
private ensureKey(): Buffer {
|
||||
mkdirSync(this.profileDir, { recursive: true });
|
||||
if (!existsSync(this.keyPath)) {
|
||||
const nextKey = randomBytes(32);
|
||||
writeFileSync(this.keyPath, nextKey);
|
||||
chmodSync(this.keyPath, 0o600);
|
||||
return nextKey;
|
||||
}
|
||||
return readFileSync(this.keyPath);
|
||||
}
|
||||
|
||||
private canUseSafeStorage() {
|
||||
return typeof safeStorage?.isEncryptionAvailable === "function" && safeStorage.isEncryptionAvailable();
|
||||
}
|
||||
|
||||
private allowInsecureVaultFallback() {
|
||||
return (
|
||||
process.env.AGENTCOMPANY_ALLOW_INSECURE_VAULT === "true" ||
|
||||
process.env.NODE_ENV === "test" ||
|
||||
process.env.VITEST === "true"
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user