@caupulican/pi-adaptative 0.93.11 → 0.93.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/dist/core/agent-paths.d.ts +6 -0
- package/dist/core/agent-paths.d.ts.map +1 -1
- package/dist/core/agent-paths.js +15 -0
- package/dist/core/agent-paths.js.map +1 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +2 -0
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/background-lane-controller.d.ts +2 -0
- package/dist/core/background-lane-controller.d.ts.map +1 -1
- package/dist/core/background-lane-controller.js +3 -0
- package/dist/core/background-lane-controller.js.map +1 -1
- package/dist/core/background-tool-task-controller.d.ts +4 -0
- package/dist/core/background-tool-task-controller.d.ts.map +1 -1
- package/dist/core/background-tool-task-controller.js +22 -19
- package/dist/core/background-tool-task-controller.js.map +1 -1
- package/dist/core/delegation/worker-agent-control-coordinator.d.ts +3 -1
- package/dist/core/delegation/worker-agent-control-coordinator.d.ts.map +1 -1
- package/dist/core/delegation/worker-agent-control-coordinator.js +8 -0
- package/dist/core/delegation/worker-agent-control-coordinator.js.map +1 -1
- package/dist/core/delegation/worker-attempt-executor.d.ts +2 -1
- package/dist/core/delegation/worker-attempt-executor.d.ts.map +1 -1
- package/dist/core/delegation/worker-attempt-executor.js +14 -1
- package/dist/core/delegation/worker-attempt-executor.js.map +1 -1
- package/dist/core/delegation/worker-claim.d.ts +4 -1
- package/dist/core/delegation/worker-claim.d.ts.map +1 -1
- package/dist/core/delegation/worker-claim.js +20 -2
- package/dist/core/delegation/worker-claim.js.map +1 -1
- package/dist/core/delegation/worker-delegation-controller.d.ts.map +1 -1
- package/dist/core/delegation/worker-delegation-controller.js +2 -0
- package/dist/core/delegation/worker-delegation-controller.js.map +1 -1
- package/dist/core/delegation/worker-lifecycle.d.ts +1 -0
- package/dist/core/delegation/worker-lifecycle.d.ts.map +1 -1
- package/dist/core/delegation/worker-lifecycle.js +3 -0
- package/dist/core/delegation/worker-lifecycle.js.map +1 -1
- package/dist/core/delegation/worker-runner.d.ts.map +1 -1
- package/dist/core/delegation/worker-runner.js +15 -9
- package/dist/core/delegation/worker-runner.js.map +1 -1
- package/dist/core/delegation/worker-terminal-output-artifact.d.ts +14 -0
- package/dist/core/delegation/worker-terminal-output-artifact.d.ts.map +1 -0
- package/dist/core/delegation/worker-terminal-output-artifact.js +49 -0
- package/dist/core/delegation/worker-terminal-output-artifact.js.map +1 -0
- package/dist/core/foreground-terminal-handoff-controller.d.ts +3 -0
- package/dist/core/foreground-terminal-handoff-controller.d.ts.map +1 -1
- package/dist/core/foreground-terminal-handoff-controller.js +6 -0
- package/dist/core/foreground-terminal-handoff-controller.js.map +1 -1
- package/dist/core/goals/goal-session-controller.d.ts +2 -2
- package/dist/core/goals/goal-session-controller.d.ts.map +1 -1
- package/dist/core/goals/goal-session-controller.js +2 -11
- package/dist/core/goals/goal-session-controller.js.map +1 -1
- package/dist/core/orchestration/contracts.d.ts +2 -0
- package/dist/core/orchestration/contracts.d.ts.map +1 -1
- package/dist/core/orchestration/contracts.js +3 -0
- package/dist/core/orchestration/contracts.js.map +1 -1
- package/dist/core/orchestration/task-runtime-codecs.d.ts.map +1 -1
- package/dist/core/orchestration/task-runtime-codecs.js +11 -2
- package/dist/core/orchestration/task-runtime-codecs.js.map +1 -1
- package/dist/core/orchestration/worker-result-adapter.d.ts +2 -1
- package/dist/core/orchestration/worker-result-adapter.d.ts.map +1 -1
- package/dist/core/orchestration/worker-result-adapter.js +41 -7
- package/dist/core/orchestration/worker-result-adapter.js.map +1 -1
- package/dist/core/runtime-builder.d.ts +5 -2
- package/dist/core/runtime-builder.d.ts.map +1 -1
- package/dist/core/runtime-builder.js +26 -8
- package/dist/core/runtime-builder.js.map +1 -1
- package/dist/core/secrets/bitwarden-credential-storage-router.d.ts +22 -0
- package/dist/core/secrets/bitwarden-credential-storage-router.d.ts.map +1 -0
- package/dist/core/secrets/bitwarden-credential-storage-router.js +59 -0
- package/dist/core/secrets/bitwarden-credential-storage-router.js.map +1 -0
- package/dist/core/secrets/bitwarden-credential-storage.d.ts +2 -8
- package/dist/core/secrets/bitwarden-credential-storage.d.ts.map +1 -1
- package/dist/core/secrets/bitwarden-credential-storage.js +23 -134
- package/dist/core/secrets/bitwarden-credential-storage.js.map +1 -1
- package/dist/core/secrets/bitwarden-secrets-manager-storage.d.ts +46 -0
- package/dist/core/secrets/bitwarden-secrets-manager-storage.d.ts.map +1 -0
- package/dist/core/secrets/bitwarden-secrets-manager-storage.js +379 -0
- package/dist/core/secrets/bitwarden-secrets-manager-storage.js.map +1 -0
- package/dist/core/secrets/credential-cli-command.d.ts +22 -0
- package/dist/core/secrets/credential-cli-command.d.ts.map +1 -0
- package/dist/core/secrets/credential-cli-command.js +82 -0
- package/dist/core/secrets/credential-cli-command.js.map +1 -0
- package/dist/core/secrets/credential-exposure-guard.d.ts +1 -1
- package/dist/core/secrets/credential-exposure-guard.d.ts.map +1 -1
- package/dist/core/secrets/credential-exposure-guard.js +5 -1
- package/dist/core/secrets/credential-exposure-guard.js.map +1 -1
- package/dist/core/secrets/credential-machine-session.d.ts +13 -0
- package/dist/core/secrets/credential-machine-session.d.ts.map +1 -0
- package/dist/core/secrets/credential-machine-session.js +139 -0
- package/dist/core/secrets/credential-machine-session.js.map +1 -0
- package/dist/core/secrets/credential-manager.d.ts +11 -1
- package/dist/core/secrets/credential-manager.d.ts.map +1 -1
- package/dist/core/secrets/credential-manager.js +44 -9
- package/dist/core/secrets/credential-manager.js.map +1 -1
- package/dist/core/secrets/credential-profile-envelope.d.ts +10 -0
- package/dist/core/secrets/credential-profile-envelope.d.ts.map +1 -0
- package/dist/core/secrets/credential-profile-envelope.js +78 -0
- package/dist/core/secrets/credential-profile-envelope.js.map +1 -0
- package/dist/core/secrets/credential-session-connection.d.ts.map +1 -1
- package/dist/core/secrets/credential-session-connection.js +2 -1
- package/dist/core/secrets/credential-session-connection.js.map +1 -1
- package/dist/core/secrets/credential-source-discovery.d.ts +7 -3
- package/dist/core/secrets/credential-source-discovery.d.ts.map +1 -1
- package/dist/core/secrets/credential-source-discovery.js +48 -9
- package/dist/core/secrets/credential-source-discovery.js.map +1 -1
- package/dist/core/tools/delegate-status.d.ts +4 -0
- package/dist/core/tools/delegate-status.d.ts.map +1 -1
- package/dist/core/tools/delegate-status.js +20 -4
- package/dist/core/tools/delegate-status.js.map +1 -1
- package/dist/core/tools/goal.d.ts +2 -2
- package/dist/core/tools/goal.d.ts.map +1 -1
- package/dist/core/tools/goal.js +30 -11
- package/dist/core/tools/goal.js.map +1 -1
- package/dist/core/tools/secret-store.d.ts.map +1 -1
- package/dist/core/tools/secret-store.js +6 -15
- package/dist/core/tools/secret-store.js.map +1 -1
- package/dist/core/tools/tool-task.d.ts.map +1 -1
- package/dist/core/tools/tool-task.js +6 -3
- package/dist/core/tools/tool-task.js.map +1 -1
- package/dist/core/util/bounded-value.d.ts +2 -0
- package/dist/core/util/bounded-value.d.ts.map +1 -1
- package/dist/core/util/bounded-value.js +13 -0
- package/dist/core/util/bounded-value.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/tools-manager.d.ts +2 -1
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +29 -0
- package/dist/utils/tools-manager.js.map +1 -1
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +12 -12
- package/package.json +4 -4
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
|
|
2
|
+
import { ensureToolWithDiagnostics } from "../../utils/tools-manager.js";
|
|
3
|
+
import { withFileLock } from "../util/atomic-file.js";
|
|
4
|
+
import { runCredentialCliCommand } from "./credential-cli-command.js";
|
|
5
|
+
import { CredentialStorageError, validateCredentialProfileName, validateCredentialProfileRecord, } from "./credential-manager.js";
|
|
6
|
+
import { CREDENTIAL_PROFILE_KEY_PREFIX, parseCredentialProfileEnvelope, serializeCredentialProfileEnvelope, summarizeCredentialProfiles, } from "./credential-profile-envelope.js";
|
|
7
|
+
const PROJECT_NAME = "Pi Credential Profiles";
|
|
8
|
+
const DATA_KEY_NAME = "Pi credential store · encryption key";
|
|
9
|
+
const DATA_KEY_NOTE = "Pi-managed profile encryption key";
|
|
10
|
+
const PROFILE_NOTE = "Pi-managed encrypted credential profile";
|
|
11
|
+
const DATA_KEY_PREFIX = "pi-credential-key:v1:";
|
|
12
|
+
const CIPHERTEXT_PREFIX = "pi-credential-profile:v1:";
|
|
13
|
+
const MAX_PROVIDER_ITEMS = 256;
|
|
14
|
+
const MAX_CIPHERTEXT_BYTES = 2 * 1024 * 1024;
|
|
15
|
+
const SYNC_FRESHNESS_MS = 30_000;
|
|
16
|
+
const PROVIDER_COORDINATION_LOCK_OPTIONS = Object.freeze({
|
|
17
|
+
retries: 60,
|
|
18
|
+
minRetryDelayMs: 50,
|
|
19
|
+
maxRetryDelayMs: 500,
|
|
20
|
+
});
|
|
21
|
+
const PROVIDER_ID_PATTERN = /^[a-f0-9]{8}-[a-f0-9]{4}-[1-8][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/iu;
|
|
22
|
+
export class BitwardenSecretsManagerStorageError extends CredentialStorageError {
|
|
23
|
+
constructor(code, message) {
|
|
24
|
+
super(code, message);
|
|
25
|
+
this.name = "BitwardenSecretsManagerStorageError";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function isObject(value) {
|
|
29
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
30
|
+
}
|
|
31
|
+
function parseJson(value) {
|
|
32
|
+
try {
|
|
33
|
+
return JSON.parse(value);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
throw new BitwardenSecretsManagerStorageError("malformed_provider_data", "Bitwarden Secrets Manager returned malformed data.");
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function parseProject(value) {
|
|
40
|
+
if (!isObject(value) ||
|
|
41
|
+
typeof value.id !== "string" ||
|
|
42
|
+
!PROVIDER_ID_PATTERN.test(value.id) ||
|
|
43
|
+
typeof value.organizationId !== "string" ||
|
|
44
|
+
!PROVIDER_ID_PATTERN.test(value.organizationId) ||
|
|
45
|
+
typeof value.name !== "string") {
|
|
46
|
+
throw new BitwardenSecretsManagerStorageError("malformed_provider_data", "Bitwarden Secrets Manager returned malformed project data.");
|
|
47
|
+
}
|
|
48
|
+
return { id: value.id, organizationId: value.organizationId, name: value.name };
|
|
49
|
+
}
|
|
50
|
+
function parseSecret(value, expectedProjectId) {
|
|
51
|
+
if (!isObject(value) ||
|
|
52
|
+
typeof value.id !== "string" ||
|
|
53
|
+
!PROVIDER_ID_PATTERN.test(value.id) ||
|
|
54
|
+
value.projectId !== expectedProjectId ||
|
|
55
|
+
typeof value.key !== "string" ||
|
|
56
|
+
typeof value.value !== "string" ||
|
|
57
|
+
Buffer.byteLength(value.value, "utf8") > MAX_CIPHERTEXT_BYTES) {
|
|
58
|
+
throw new BitwardenSecretsManagerStorageError("malformed_provider_data", "Bitwarden Secrets Manager returned malformed secret data.");
|
|
59
|
+
}
|
|
60
|
+
return { id: value.id, projectId: expectedProjectId, key: value.key, value: value.value };
|
|
61
|
+
}
|
|
62
|
+
function decodeBase64(value, expectedBytes) {
|
|
63
|
+
if (!/^[A-Za-z0-9+/]+={0,2}$/u.test(value)) {
|
|
64
|
+
throw new BitwardenSecretsManagerStorageError("malformed_provider_data", "Bitwarden Secrets Manager returned malformed encrypted profile data.");
|
|
65
|
+
}
|
|
66
|
+
const decoded = Buffer.from(value, "base64");
|
|
67
|
+
if ((expectedBytes !== undefined && decoded.length !== expectedBytes) || decoded.toString("base64") !== value) {
|
|
68
|
+
decoded.fill(0);
|
|
69
|
+
throw new BitwardenSecretsManagerStorageError("malformed_provider_data", "Bitwarden Secrets Manager returned malformed encrypted profile data.");
|
|
70
|
+
}
|
|
71
|
+
return decoded;
|
|
72
|
+
}
|
|
73
|
+
function parseDataKey(value) {
|
|
74
|
+
if (!value.startsWith(DATA_KEY_PREFIX)) {
|
|
75
|
+
throw new BitwardenSecretsManagerStorageError("malformed_provider_data", "Bitwarden Secrets Manager returned a malformed profile encryption key.");
|
|
76
|
+
}
|
|
77
|
+
return decodeBase64(value.slice(DATA_KEY_PREFIX.length), 32);
|
|
78
|
+
}
|
|
79
|
+
function encryptProfile(record, dataKey) {
|
|
80
|
+
let serialized = serializeCredentialProfileEnvelope(record);
|
|
81
|
+
const plaintext = Buffer.from(serialized, "utf8");
|
|
82
|
+
const iv = randomBytes(12);
|
|
83
|
+
try {
|
|
84
|
+
const cipher = createCipheriv("aes-256-gcm", dataKey, iv);
|
|
85
|
+
const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
86
|
+
const tag = cipher.getAuthTag();
|
|
87
|
+
try {
|
|
88
|
+
return `${CIPHERTEXT_PREFIX}${iv.toString("base64")}:${tag.toString("base64")}:${ciphertext.toString("base64")}`;
|
|
89
|
+
}
|
|
90
|
+
finally {
|
|
91
|
+
ciphertext.fill(0);
|
|
92
|
+
tag.fill(0);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
serialized = "";
|
|
97
|
+
plaintext.fill(0);
|
|
98
|
+
iv.fill(0);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function decryptProfile(value, profile, dataKey) {
|
|
102
|
+
if (!value.startsWith(CIPHERTEXT_PREFIX) || Buffer.byteLength(value, "utf8") > MAX_CIPHERTEXT_BYTES) {
|
|
103
|
+
throw new BitwardenSecretsManagerStorageError("malformed_provider_data", "Bitwarden Secrets Manager returned malformed encrypted profile data.");
|
|
104
|
+
}
|
|
105
|
+
const encoded = value.slice(CIPHERTEXT_PREFIX.length).split(":");
|
|
106
|
+
if (encoded.length !== 3) {
|
|
107
|
+
throw new BitwardenSecretsManagerStorageError("malformed_provider_data", "Bitwarden Secrets Manager returned malformed encrypted profile data.");
|
|
108
|
+
}
|
|
109
|
+
const iv = decodeBase64(encoded[0] ?? "", 12);
|
|
110
|
+
const tag = decodeBase64(encoded[1] ?? "", 16);
|
|
111
|
+
const ciphertext = decodeBase64(encoded[2] ?? "");
|
|
112
|
+
let plaintext = Buffer.alloc(0);
|
|
113
|
+
let serialized = "";
|
|
114
|
+
try {
|
|
115
|
+
const decipher = createDecipheriv("aes-256-gcm", dataKey, iv);
|
|
116
|
+
decipher.setAuthTag(tag);
|
|
117
|
+
plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
118
|
+
serialized = plaintext.toString("utf8");
|
|
119
|
+
return parseCredentialProfileEnvelope(serialized, profile);
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
if (error instanceof BitwardenSecretsManagerStorageError)
|
|
123
|
+
throw error;
|
|
124
|
+
throw new BitwardenSecretsManagerStorageError("malformed_provider_data", "Bitwarden Secrets Manager returned malformed encrypted profile data.");
|
|
125
|
+
}
|
|
126
|
+
finally {
|
|
127
|
+
iv.fill(0);
|
|
128
|
+
tag.fill(0);
|
|
129
|
+
ciphertext.fill(0);
|
|
130
|
+
plaintext.fill(0);
|
|
131
|
+
serialized = "";
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
export async function runBitwardenSecretsManagerCommand(request) {
|
|
135
|
+
return runCredentialCliCommand({
|
|
136
|
+
executable: request.executable,
|
|
137
|
+
args: request.args,
|
|
138
|
+
authEnvironment: { name: "BWS_ACCESS_TOKEN", value: request.accessToken },
|
|
139
|
+
omitEnvironmentVariables: ["BW_SESSION"],
|
|
140
|
+
...(request.signal ? { signal: request.signal } : {}),
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
export class BitwardenSecretsManagerCredentialStorage {
|
|
144
|
+
constructor(options) {
|
|
145
|
+
this.profiles = new Map();
|
|
146
|
+
this.lastSyncAt = 0;
|
|
147
|
+
if (options.coordinationFile.trim().length === 0) {
|
|
148
|
+
throw new TypeError("Bitwarden Secrets Manager requires a coordination file.");
|
|
149
|
+
}
|
|
150
|
+
this.coordinationFile = options.coordinationFile;
|
|
151
|
+
this.resolveTool = options.resolveTool ?? ensureToolWithDiagnostics;
|
|
152
|
+
this.runCommand = options.runCommand ?? runBitwardenSecretsManagerCommand;
|
|
153
|
+
}
|
|
154
|
+
async connect(accessToken, signal) {
|
|
155
|
+
this.lock();
|
|
156
|
+
const resolution = await this.resolveTool("bws", true);
|
|
157
|
+
if (resolution.status === "unavailable") {
|
|
158
|
+
throw new BitwardenSecretsManagerStorageError("provider_unavailable", `Bitwarden Secrets Manager CLI could not be provisioned (${resolution.failureCode}).`);
|
|
159
|
+
}
|
|
160
|
+
this.executable = resolution.path;
|
|
161
|
+
this.accessToken = accessToken;
|
|
162
|
+
try {
|
|
163
|
+
await this.reload(signal);
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
this.lock();
|
|
167
|
+
throw error;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
async refresh(signal) {
|
|
171
|
+
this.assertConnected();
|
|
172
|
+
if (Date.now() - this.lastSyncAt >= SYNC_FRESHNESS_MS)
|
|
173
|
+
await this.reload(signal);
|
|
174
|
+
}
|
|
175
|
+
async listProfiles() {
|
|
176
|
+
this.assertConnected();
|
|
177
|
+
return summarizeCredentialProfiles([...this.profiles.values()].map((profile) => profile.record));
|
|
178
|
+
}
|
|
179
|
+
async readProfile(profile) {
|
|
180
|
+
this.assertConnected();
|
|
181
|
+
const cached = this.profiles.get(profile);
|
|
182
|
+
if (!cached) {
|
|
183
|
+
throw new BitwardenSecretsManagerStorageError("profile_not_found", "Credential profile was not found.");
|
|
184
|
+
}
|
|
185
|
+
return structuredClone(cached.record);
|
|
186
|
+
}
|
|
187
|
+
async writeProfile(record, signal) {
|
|
188
|
+
validateCredentialProfileRecord(record);
|
|
189
|
+
this.assertConnected();
|
|
190
|
+
await this.withProviderCoordination(signal, async () => {
|
|
191
|
+
await this.reloadUnderCoordination(signal);
|
|
192
|
+
const { project } = this.assertConnected();
|
|
193
|
+
const dataKey = await this.ensureDataKeyUnderCoordination(signal);
|
|
194
|
+
let ciphertext = encryptProfile(record, dataKey);
|
|
195
|
+
try {
|
|
196
|
+
const existing = this.profiles.get(record.profile);
|
|
197
|
+
const key = `${CREDENTIAL_PROFILE_KEY_PREFIX}${record.profile}`;
|
|
198
|
+
const output = existing
|
|
199
|
+
? await this.runChecked([
|
|
200
|
+
"secret",
|
|
201
|
+
"edit",
|
|
202
|
+
existing.id,
|
|
203
|
+
"--key",
|
|
204
|
+
key,
|
|
205
|
+
"--value",
|
|
206
|
+
ciphertext,
|
|
207
|
+
"--note",
|
|
208
|
+
PROFILE_NOTE,
|
|
209
|
+
"--project-id",
|
|
210
|
+
project.id,
|
|
211
|
+
], signal)
|
|
212
|
+
: await this.runChecked(["secret", "create", key, ciphertext, project.id, "--note", PROFILE_NOTE], signal);
|
|
213
|
+
const stored = parseSecret(parseJson(output), project.id);
|
|
214
|
+
if (stored.key !== key || stored.value !== ciphertext) {
|
|
215
|
+
throw new BitwardenSecretsManagerStorageError("malformed_provider_data", "Bitwarden Secrets Manager returned malformed profile data.");
|
|
216
|
+
}
|
|
217
|
+
this.profiles.set(record.profile, { id: stored.id, record: structuredClone(record) });
|
|
218
|
+
this.lastSyncAt = Date.now();
|
|
219
|
+
}
|
|
220
|
+
finally {
|
|
221
|
+
ciphertext = "";
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
async deleteProfile(profile, signal) {
|
|
226
|
+
this.assertConnected();
|
|
227
|
+
await this.withProviderCoordination(signal, async () => {
|
|
228
|
+
await this.reloadUnderCoordination(signal);
|
|
229
|
+
const cached = this.profiles.get(profile);
|
|
230
|
+
if (!cached) {
|
|
231
|
+
throw new BitwardenSecretsManagerStorageError("profile_not_found", "Credential profile was not found.");
|
|
232
|
+
}
|
|
233
|
+
await this.runChecked(["secret", "delete", cached.id], signal);
|
|
234
|
+
for (const variable of cached.record.variables)
|
|
235
|
+
variable.value = "";
|
|
236
|
+
this.profiles.delete(profile);
|
|
237
|
+
this.lastSyncAt = Date.now();
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
lock() {
|
|
241
|
+
this.executable = undefined;
|
|
242
|
+
this.accessToken = undefined;
|
|
243
|
+
this.project = undefined;
|
|
244
|
+
this.dataKey?.fill(0);
|
|
245
|
+
this.dataKey = undefined;
|
|
246
|
+
for (const cached of this.profiles.values()) {
|
|
247
|
+
for (const variable of cached.record.variables)
|
|
248
|
+
variable.value = "";
|
|
249
|
+
}
|
|
250
|
+
this.profiles.clear();
|
|
251
|
+
this.lastSyncAt = 0;
|
|
252
|
+
}
|
|
253
|
+
assertConnected() {
|
|
254
|
+
if (!this.executable || !this.accessToken || !this.project) {
|
|
255
|
+
throw new BitwardenSecretsManagerStorageError("not_connected", "Bitwarden is not connected.");
|
|
256
|
+
}
|
|
257
|
+
return { executable: this.executable, accessToken: this.accessToken, project: this.project };
|
|
258
|
+
}
|
|
259
|
+
async reload(signal) {
|
|
260
|
+
await this.withProviderCoordination(signal, () => this.reloadUnderCoordination(signal));
|
|
261
|
+
}
|
|
262
|
+
async reloadUnderCoordination(signal) {
|
|
263
|
+
const projectsValue = parseJson(await this.runChecked(["project", "list"], signal));
|
|
264
|
+
if (!Array.isArray(projectsValue) || projectsValue.length > MAX_PROVIDER_ITEMS) {
|
|
265
|
+
throw new BitwardenSecretsManagerStorageError("malformed_provider_data", "Bitwarden Secrets Manager returned an invalid project list.");
|
|
266
|
+
}
|
|
267
|
+
const matchingProjects = projectsValue.map(parseProject).filter((project) => project.name === PROJECT_NAME);
|
|
268
|
+
if (matchingProjects.length > 1) {
|
|
269
|
+
throw new BitwardenSecretsManagerStorageError("malformed_provider_data", "Bitwarden Secrets Manager contains duplicate Pi credential projects.");
|
|
270
|
+
}
|
|
271
|
+
const project = matchingProjects[0] ??
|
|
272
|
+
parseProject(parseJson(await this.runChecked(["project", "create", PROJECT_NAME], signal)));
|
|
273
|
+
if (project.name !== PROJECT_NAME) {
|
|
274
|
+
throw new BitwardenSecretsManagerStorageError("malformed_provider_data", "Bitwarden Secrets Manager returned the wrong credential project.");
|
|
275
|
+
}
|
|
276
|
+
this.project = project;
|
|
277
|
+
const secretsValue = parseJson(await this.runChecked(["secret", "list", project.id], signal));
|
|
278
|
+
if (!Array.isArray(secretsValue) || secretsValue.length > MAX_PROVIDER_ITEMS) {
|
|
279
|
+
throw new BitwardenSecretsManagerStorageError("malformed_provider_data", "Bitwarden Secrets Manager returned an invalid secret list.");
|
|
280
|
+
}
|
|
281
|
+
const secrets = secretsValue.map((secret) => parseSecret(secret, project.id));
|
|
282
|
+
const keySecrets = secrets.filter((secret) => secret.key === DATA_KEY_NAME);
|
|
283
|
+
if (keySecrets.length > 1) {
|
|
284
|
+
throw new BitwardenSecretsManagerStorageError("malformed_provider_data", "Bitwarden Secrets Manager contains duplicate Pi encryption keys.");
|
|
285
|
+
}
|
|
286
|
+
const nextKey = keySecrets[0] ? parseDataKey(keySecrets[0].value) : undefined;
|
|
287
|
+
const nextProfiles = new Map();
|
|
288
|
+
try {
|
|
289
|
+
for (const secret of secrets) {
|
|
290
|
+
if (!secret.key.startsWith(CREDENTIAL_PROFILE_KEY_PREFIX))
|
|
291
|
+
continue;
|
|
292
|
+
if (!nextKey) {
|
|
293
|
+
throw new BitwardenSecretsManagerStorageError("malformed_provider_data", "Bitwarden Secrets Manager profile encryption key is missing.");
|
|
294
|
+
}
|
|
295
|
+
const profile = secret.key.slice(CREDENTIAL_PROFILE_KEY_PREFIX.length);
|
|
296
|
+
try {
|
|
297
|
+
validateCredentialProfileName(profile);
|
|
298
|
+
}
|
|
299
|
+
catch {
|
|
300
|
+
throw new BitwardenSecretsManagerStorageError("malformed_provider_data", "Bitwarden Secrets Manager returned malformed profile data.");
|
|
301
|
+
}
|
|
302
|
+
if (nextProfiles.has(profile)) {
|
|
303
|
+
throw new BitwardenSecretsManagerStorageError("malformed_provider_data", "Bitwarden Secrets Manager contains duplicate Pi credential profiles.");
|
|
304
|
+
}
|
|
305
|
+
nextProfiles.set(profile, { id: secret.id, record: decryptProfile(secret.value, profile, nextKey) });
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
catch (error) {
|
|
309
|
+
for (const cached of nextProfiles.values()) {
|
|
310
|
+
for (const variable of cached.record.variables)
|
|
311
|
+
variable.value = "";
|
|
312
|
+
}
|
|
313
|
+
nextKey?.fill(0);
|
|
314
|
+
throw error;
|
|
315
|
+
}
|
|
316
|
+
this.dataKey?.fill(0);
|
|
317
|
+
this.dataKey = nextKey;
|
|
318
|
+
for (const cached of this.profiles.values()) {
|
|
319
|
+
for (const variable of cached.record.variables)
|
|
320
|
+
variable.value = "";
|
|
321
|
+
}
|
|
322
|
+
this.profiles.clear();
|
|
323
|
+
for (const [profile, cached] of nextProfiles)
|
|
324
|
+
this.profiles.set(profile, cached);
|
|
325
|
+
this.lastSyncAt = Date.now();
|
|
326
|
+
}
|
|
327
|
+
async ensureDataKeyUnderCoordination(signal) {
|
|
328
|
+
if (this.dataKey)
|
|
329
|
+
return this.dataKey;
|
|
330
|
+
const { project } = this.assertConnected();
|
|
331
|
+
const generated = randomBytes(32);
|
|
332
|
+
let serialized = `${DATA_KEY_PREFIX}${generated.toString("base64")}`;
|
|
333
|
+
try {
|
|
334
|
+
const stored = parseSecret(parseJson(await this.runChecked(["secret", "create", DATA_KEY_NAME, serialized, project.id, "--note", DATA_KEY_NOTE], signal)), project.id);
|
|
335
|
+
if (stored.key !== DATA_KEY_NAME || stored.value !== serialized) {
|
|
336
|
+
throw new BitwardenSecretsManagerStorageError("malformed_provider_data", "Bitwarden Secrets Manager returned a malformed profile encryption key.");
|
|
337
|
+
}
|
|
338
|
+
this.dataKey = Buffer.from(generated);
|
|
339
|
+
return this.dataKey;
|
|
340
|
+
}
|
|
341
|
+
finally {
|
|
342
|
+
generated.fill(0);
|
|
343
|
+
serialized = "";
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
async withProviderCoordination(signal, operation) {
|
|
347
|
+
signal?.throwIfAborted();
|
|
348
|
+
try {
|
|
349
|
+
return await withFileLock(this.coordinationFile, async () => {
|
|
350
|
+
signal?.throwIfAborted();
|
|
351
|
+
return operation();
|
|
352
|
+
}, PROVIDER_COORDINATION_LOCK_OPTIONS);
|
|
353
|
+
}
|
|
354
|
+
catch (error) {
|
|
355
|
+
if (error instanceof CredentialStorageError || signal?.aborted)
|
|
356
|
+
throw error;
|
|
357
|
+
throw new BitwardenSecretsManagerStorageError("provider_command_failed", "Bitwarden Secrets Manager coordination failed safely.");
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
async runChecked(args, signal) {
|
|
361
|
+
if (!this.executable || !this.accessToken) {
|
|
362
|
+
throw new BitwardenSecretsManagerStorageError("not_connected", "Bitwarden is not connected.");
|
|
363
|
+
}
|
|
364
|
+
const result = await this.runCommand({
|
|
365
|
+
executable: this.executable,
|
|
366
|
+
args,
|
|
367
|
+
accessToken: this.accessToken,
|
|
368
|
+
...(signal ? { signal } : {}),
|
|
369
|
+
});
|
|
370
|
+
if (result.exitCode !== 0 ||
|
|
371
|
+
result.aborted === true ||
|
|
372
|
+
result.timedOut === true ||
|
|
373
|
+
result.outputLimitExceeded === true) {
|
|
374
|
+
throw new BitwardenSecretsManagerStorageError("provider_command_failed", "Bitwarden Secrets Manager command failed safely without exposing provider output.");
|
|
375
|
+
}
|
|
376
|
+
return result.stdout;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
//# sourceMappingURL=bitwarden-secrets-manager-storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bitwarden-secrets-manager-storage.js","sourceRoot":"","sources":["../../../src/core/secrets/bitwarden-secrets-manager-storage.ts"],"names":[],"mappings":"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","sourcesContent":["import { createCipheriv, createDecipheriv, randomBytes } from \"node:crypto\";\nimport { ensureToolWithDiagnostics, type ManagedToolResolver } from \"../../utils/tools-manager.ts\";\nimport { type AtomicFileLockOptions, withFileLock } from \"../util/atomic-file.ts\";\nimport { type CredentialCliCommandResult, runCredentialCliCommand } from \"./credential-cli-command.ts\";\nimport {\n\ttype CredentialProfileRecord,\n\ttype CredentialProfileStorage,\n\tCredentialStorageError,\n\ttype StoredCredentialProfileSummary,\n\tvalidateCredentialProfileName,\n\tvalidateCredentialProfileRecord,\n} from \"./credential-manager.ts\";\nimport {\n\tCREDENTIAL_PROFILE_KEY_PREFIX,\n\tparseCredentialProfileEnvelope,\n\tserializeCredentialProfileEnvelope,\n\tsummarizeCredentialProfiles,\n} from \"./credential-profile-envelope.ts\";\n\nconst PROJECT_NAME = \"Pi Credential Profiles\";\nconst DATA_KEY_NAME = \"Pi credential store · encryption key\";\nconst DATA_KEY_NOTE = \"Pi-managed profile encryption key\";\nconst PROFILE_NOTE = \"Pi-managed encrypted credential profile\";\nconst DATA_KEY_PREFIX = \"pi-credential-key:v1:\";\nconst CIPHERTEXT_PREFIX = \"pi-credential-profile:v1:\";\nconst MAX_PROVIDER_ITEMS = 256;\nconst MAX_CIPHERTEXT_BYTES = 2 * 1024 * 1024;\nconst SYNC_FRESHNESS_MS = 30_000;\nconst PROVIDER_COORDINATION_LOCK_OPTIONS: Readonly<AtomicFileLockOptions> = Object.freeze({\n\tretries: 60,\n\tminRetryDelayMs: 50,\n\tmaxRetryDelayMs: 500,\n});\nconst PROVIDER_ID_PATTERN = /^[a-f0-9]{8}-[a-f0-9]{4}-[1-8][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/iu;\n\nexport class BitwardenSecretsManagerStorageError extends CredentialStorageError {\n\tconstructor(code: CredentialStorageError[\"code\"], message: string) {\n\t\tsuper(code, message);\n\t\tthis.name = \"BitwardenSecretsManagerStorageError\";\n\t}\n}\n\nexport interface BitwardenSecretsManagerCommandRequest {\n\texecutable: string;\n\targs: string[];\n\taccessToken: string;\n\tsignal?: AbortSignal;\n}\n\nexport type BitwardenSecretsManagerCommandResult = CredentialCliCommandResult;\n\nexport type BitwardenSecretsManagerCommandRunner = (\n\trequest: BitwardenSecretsManagerCommandRequest,\n) => Promise<BitwardenSecretsManagerCommandResult>;\n\nexport interface BitwardenSecretsManagerCredentialStorageOptions {\n\tcoordinationFile: string;\n\tresolveTool?: ManagedToolResolver;\n\trunCommand?: BitwardenSecretsManagerCommandRunner;\n}\n\ninterface SecretsManagerProject {\n\tid: string;\n\torganizationId: string;\n\tname: string;\n}\n\ninterface SecretsManagerSecret {\n\tid: string;\n\tprojectId: string;\n\tkey: string;\n\tvalue: string;\n}\n\ninterface CachedSecretsManagerProfile {\n\tid: string;\n\trecord: CredentialProfileRecord;\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction parseJson(value: string): unknown {\n\ttry {\n\t\treturn JSON.parse(value) as unknown;\n\t} catch {\n\t\tthrow new BitwardenSecretsManagerStorageError(\n\t\t\t\"malformed_provider_data\",\n\t\t\t\"Bitwarden Secrets Manager returned malformed data.\",\n\t\t);\n\t}\n}\n\nfunction parseProject(value: unknown): SecretsManagerProject {\n\tif (\n\t\t!isObject(value) ||\n\t\ttypeof value.id !== \"string\" ||\n\t\t!PROVIDER_ID_PATTERN.test(value.id) ||\n\t\ttypeof value.organizationId !== \"string\" ||\n\t\t!PROVIDER_ID_PATTERN.test(value.organizationId) ||\n\t\ttypeof value.name !== \"string\"\n\t) {\n\t\tthrow new BitwardenSecretsManagerStorageError(\n\t\t\t\"malformed_provider_data\",\n\t\t\t\"Bitwarden Secrets Manager returned malformed project data.\",\n\t\t);\n\t}\n\treturn { id: value.id, organizationId: value.organizationId, name: value.name };\n}\n\nfunction parseSecret(value: unknown, expectedProjectId: string): SecretsManagerSecret {\n\tif (\n\t\t!isObject(value) ||\n\t\ttypeof value.id !== \"string\" ||\n\t\t!PROVIDER_ID_PATTERN.test(value.id) ||\n\t\tvalue.projectId !== expectedProjectId ||\n\t\ttypeof value.key !== \"string\" ||\n\t\ttypeof value.value !== \"string\" ||\n\t\tBuffer.byteLength(value.value, \"utf8\") > MAX_CIPHERTEXT_BYTES\n\t) {\n\t\tthrow new BitwardenSecretsManagerStorageError(\n\t\t\t\"malformed_provider_data\",\n\t\t\t\"Bitwarden Secrets Manager returned malformed secret data.\",\n\t\t);\n\t}\n\treturn { id: value.id, projectId: expectedProjectId, key: value.key, value: value.value };\n}\n\nfunction decodeBase64(value: string, expectedBytes?: number): Buffer {\n\tif (!/^[A-Za-z0-9+/]+={0,2}$/u.test(value)) {\n\t\tthrow new BitwardenSecretsManagerStorageError(\n\t\t\t\"malformed_provider_data\",\n\t\t\t\"Bitwarden Secrets Manager returned malformed encrypted profile data.\",\n\t\t);\n\t}\n\tconst decoded = Buffer.from(value, \"base64\");\n\tif ((expectedBytes !== undefined && decoded.length !== expectedBytes) || decoded.toString(\"base64\") !== value) {\n\t\tdecoded.fill(0);\n\t\tthrow new BitwardenSecretsManagerStorageError(\n\t\t\t\"malformed_provider_data\",\n\t\t\t\"Bitwarden Secrets Manager returned malformed encrypted profile data.\",\n\t\t);\n\t}\n\treturn decoded;\n}\n\nfunction parseDataKey(value: string): Buffer {\n\tif (!value.startsWith(DATA_KEY_PREFIX)) {\n\t\tthrow new BitwardenSecretsManagerStorageError(\n\t\t\t\"malformed_provider_data\",\n\t\t\t\"Bitwarden Secrets Manager returned a malformed profile encryption key.\",\n\t\t);\n\t}\n\treturn decodeBase64(value.slice(DATA_KEY_PREFIX.length), 32);\n}\n\nfunction encryptProfile(record: CredentialProfileRecord, dataKey: Buffer): string {\n\tlet serialized = serializeCredentialProfileEnvelope(record);\n\tconst plaintext = Buffer.from(serialized, \"utf8\");\n\tconst iv = randomBytes(12);\n\ttry {\n\t\tconst cipher = createCipheriv(\"aes-256-gcm\", dataKey, iv);\n\t\tconst ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);\n\t\tconst tag = cipher.getAuthTag();\n\t\ttry {\n\t\t\treturn `${CIPHERTEXT_PREFIX}${iv.toString(\"base64\")}:${tag.toString(\"base64\")}:${ciphertext.toString(\"base64\")}`;\n\t\t} finally {\n\t\t\tciphertext.fill(0);\n\t\t\ttag.fill(0);\n\t\t}\n\t} finally {\n\t\tserialized = \"\";\n\t\tplaintext.fill(0);\n\t\tiv.fill(0);\n\t}\n}\n\nfunction decryptProfile(value: string, profile: string, dataKey: Buffer): CredentialProfileRecord {\n\tif (!value.startsWith(CIPHERTEXT_PREFIX) || Buffer.byteLength(value, \"utf8\") > MAX_CIPHERTEXT_BYTES) {\n\t\tthrow new BitwardenSecretsManagerStorageError(\n\t\t\t\"malformed_provider_data\",\n\t\t\t\"Bitwarden Secrets Manager returned malformed encrypted profile data.\",\n\t\t);\n\t}\n\tconst encoded = value.slice(CIPHERTEXT_PREFIX.length).split(\":\");\n\tif (encoded.length !== 3) {\n\t\tthrow new BitwardenSecretsManagerStorageError(\n\t\t\t\"malformed_provider_data\",\n\t\t\t\"Bitwarden Secrets Manager returned malformed encrypted profile data.\",\n\t\t);\n\t}\n\tconst iv = decodeBase64(encoded[0] ?? \"\", 12);\n\tconst tag = decodeBase64(encoded[1] ?? \"\", 16);\n\tconst ciphertext = decodeBase64(encoded[2] ?? \"\");\n\tlet plaintext = Buffer.alloc(0);\n\tlet serialized = \"\";\n\ttry {\n\t\tconst decipher = createDecipheriv(\"aes-256-gcm\", dataKey, iv);\n\t\tdecipher.setAuthTag(tag);\n\t\tplaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);\n\t\tserialized = plaintext.toString(\"utf8\");\n\t\treturn parseCredentialProfileEnvelope(serialized, profile);\n\t} catch (error) {\n\t\tif (error instanceof BitwardenSecretsManagerStorageError) throw error;\n\t\tthrow new BitwardenSecretsManagerStorageError(\n\t\t\t\"malformed_provider_data\",\n\t\t\t\"Bitwarden Secrets Manager returned malformed encrypted profile data.\",\n\t\t);\n\t} finally {\n\t\tiv.fill(0);\n\t\ttag.fill(0);\n\t\tciphertext.fill(0);\n\t\tplaintext.fill(0);\n\t\tserialized = \"\";\n\t}\n}\n\nexport async function runBitwardenSecretsManagerCommand(\n\trequest: BitwardenSecretsManagerCommandRequest,\n): Promise<BitwardenSecretsManagerCommandResult> {\n\treturn runCredentialCliCommand({\n\t\texecutable: request.executable,\n\t\targs: request.args,\n\t\tauthEnvironment: { name: \"BWS_ACCESS_TOKEN\", value: request.accessToken },\n\t\tomitEnvironmentVariables: [\"BW_SESSION\"],\n\t\t...(request.signal ? { signal: request.signal } : {}),\n\t});\n}\n\nexport class BitwardenSecretsManagerCredentialStorage implements CredentialProfileStorage {\n\tprivate readonly coordinationFile: string;\n\tprivate readonly resolveTool: ManagedToolResolver;\n\tprivate readonly runCommand: BitwardenSecretsManagerCommandRunner;\n\tprivate readonly profiles = new Map<string, CachedSecretsManagerProfile>();\n\tprivate executable: string | undefined;\n\tprivate accessToken: string | undefined;\n\tprivate project: SecretsManagerProject | undefined;\n\tprivate dataKey: Buffer | undefined;\n\tprivate lastSyncAt = 0;\n\n\tconstructor(options: BitwardenSecretsManagerCredentialStorageOptions) {\n\t\tif (options.coordinationFile.trim().length === 0) {\n\t\t\tthrow new TypeError(\"Bitwarden Secrets Manager requires a coordination file.\");\n\t\t}\n\t\tthis.coordinationFile = options.coordinationFile;\n\t\tthis.resolveTool = options.resolveTool ?? ensureToolWithDiagnostics;\n\t\tthis.runCommand = options.runCommand ?? runBitwardenSecretsManagerCommand;\n\t}\n\n\tasync connect(accessToken: string, signal?: AbortSignal): Promise<void> {\n\t\tthis.lock();\n\t\tconst resolution = await this.resolveTool(\"bws\", true);\n\t\tif (resolution.status === \"unavailable\") {\n\t\t\tthrow new BitwardenSecretsManagerStorageError(\n\t\t\t\t\"provider_unavailable\",\n\t\t\t\t`Bitwarden Secrets Manager CLI could not be provisioned (${resolution.failureCode}).`,\n\t\t\t);\n\t\t}\n\t\tthis.executable = resolution.path;\n\t\tthis.accessToken = accessToken;\n\t\ttry {\n\t\t\tawait this.reload(signal);\n\t\t} catch (error) {\n\t\t\tthis.lock();\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync refresh(signal?: AbortSignal): Promise<void> {\n\t\tthis.assertConnected();\n\t\tif (Date.now() - this.lastSyncAt >= SYNC_FRESHNESS_MS) await this.reload(signal);\n\t}\n\n\tasync listProfiles(): Promise<StoredCredentialProfileSummary[]> {\n\t\tthis.assertConnected();\n\t\treturn summarizeCredentialProfiles([...this.profiles.values()].map((profile) => profile.record));\n\t}\n\n\tasync readProfile(profile: string): Promise<CredentialProfileRecord> {\n\t\tthis.assertConnected();\n\t\tconst cached = this.profiles.get(profile);\n\t\tif (!cached) {\n\t\t\tthrow new BitwardenSecretsManagerStorageError(\"profile_not_found\", \"Credential profile was not found.\");\n\t\t}\n\t\treturn structuredClone(cached.record);\n\t}\n\n\tasync writeProfile(record: CredentialProfileRecord, signal?: AbortSignal): Promise<void> {\n\t\tvalidateCredentialProfileRecord(record);\n\t\tthis.assertConnected();\n\t\tawait this.withProviderCoordination(signal, async () => {\n\t\t\tawait this.reloadUnderCoordination(signal);\n\t\t\tconst { project } = this.assertConnected();\n\t\t\tconst dataKey = await this.ensureDataKeyUnderCoordination(signal);\n\t\t\tlet ciphertext = encryptProfile(record, dataKey);\n\t\t\ttry {\n\t\t\t\tconst existing = this.profiles.get(record.profile);\n\t\t\t\tconst key = `${CREDENTIAL_PROFILE_KEY_PREFIX}${record.profile}`;\n\t\t\t\tconst output = existing\n\t\t\t\t\t? await this.runChecked(\n\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\"secret\",\n\t\t\t\t\t\t\t\t\"edit\",\n\t\t\t\t\t\t\t\texisting.id,\n\t\t\t\t\t\t\t\t\"--key\",\n\t\t\t\t\t\t\t\tkey,\n\t\t\t\t\t\t\t\t\"--value\",\n\t\t\t\t\t\t\t\tciphertext,\n\t\t\t\t\t\t\t\t\"--note\",\n\t\t\t\t\t\t\t\tPROFILE_NOTE,\n\t\t\t\t\t\t\t\t\"--project-id\",\n\t\t\t\t\t\t\t\tproject.id,\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\tsignal,\n\t\t\t\t\t\t)\n\t\t\t\t\t: await this.runChecked(\n\t\t\t\t\t\t\t[\"secret\", \"create\", key, ciphertext, project.id, \"--note\", PROFILE_NOTE],\n\t\t\t\t\t\t\tsignal,\n\t\t\t\t\t\t);\n\t\t\t\tconst stored = parseSecret(parseJson(output), project.id);\n\t\t\t\tif (stored.key !== key || stored.value !== ciphertext) {\n\t\t\t\t\tthrow new BitwardenSecretsManagerStorageError(\n\t\t\t\t\t\t\"malformed_provider_data\",\n\t\t\t\t\t\t\"Bitwarden Secrets Manager returned malformed profile data.\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tthis.profiles.set(record.profile, { id: stored.id, record: structuredClone(record) });\n\t\t\t\tthis.lastSyncAt = Date.now();\n\t\t\t} finally {\n\t\t\t\tciphertext = \"\";\n\t\t\t}\n\t\t});\n\t}\n\n\tasync deleteProfile(profile: string, signal?: AbortSignal): Promise<void> {\n\t\tthis.assertConnected();\n\t\tawait this.withProviderCoordination(signal, async () => {\n\t\t\tawait this.reloadUnderCoordination(signal);\n\t\t\tconst cached = this.profiles.get(profile);\n\t\t\tif (!cached) {\n\t\t\t\tthrow new BitwardenSecretsManagerStorageError(\"profile_not_found\", \"Credential profile was not found.\");\n\t\t\t}\n\t\t\tawait this.runChecked([\"secret\", \"delete\", cached.id], signal);\n\t\t\tfor (const variable of cached.record.variables) variable.value = \"\";\n\t\t\tthis.profiles.delete(profile);\n\t\t\tthis.lastSyncAt = Date.now();\n\t\t});\n\t}\n\n\tlock(): void {\n\t\tthis.executable = undefined;\n\t\tthis.accessToken = undefined;\n\t\tthis.project = undefined;\n\t\tthis.dataKey?.fill(0);\n\t\tthis.dataKey = undefined;\n\t\tfor (const cached of this.profiles.values()) {\n\t\t\tfor (const variable of cached.record.variables) variable.value = \"\";\n\t\t}\n\t\tthis.profiles.clear();\n\t\tthis.lastSyncAt = 0;\n\t}\n\n\tprivate assertConnected(): {\n\t\texecutable: string;\n\t\taccessToken: string;\n\t\tproject: SecretsManagerProject;\n\t} {\n\t\tif (!this.executable || !this.accessToken || !this.project) {\n\t\t\tthrow new BitwardenSecretsManagerStorageError(\"not_connected\", \"Bitwarden is not connected.\");\n\t\t}\n\t\treturn { executable: this.executable, accessToken: this.accessToken, project: this.project };\n\t}\n\n\tprivate async reload(signal?: AbortSignal): Promise<void> {\n\t\tawait this.withProviderCoordination(signal, () => this.reloadUnderCoordination(signal));\n\t}\n\n\tprivate async reloadUnderCoordination(signal?: AbortSignal): Promise<void> {\n\t\tconst projectsValue = parseJson(await this.runChecked([\"project\", \"list\"], signal));\n\t\tif (!Array.isArray(projectsValue) || projectsValue.length > MAX_PROVIDER_ITEMS) {\n\t\t\tthrow new BitwardenSecretsManagerStorageError(\n\t\t\t\t\"malformed_provider_data\",\n\t\t\t\t\"Bitwarden Secrets Manager returned an invalid project list.\",\n\t\t\t);\n\t\t}\n\t\tconst matchingProjects = projectsValue.map(parseProject).filter((project) => project.name === PROJECT_NAME);\n\t\tif (matchingProjects.length > 1) {\n\t\t\tthrow new BitwardenSecretsManagerStorageError(\n\t\t\t\t\"malformed_provider_data\",\n\t\t\t\t\"Bitwarden Secrets Manager contains duplicate Pi credential projects.\",\n\t\t\t);\n\t\t}\n\t\tconst project =\n\t\t\tmatchingProjects[0] ??\n\t\t\tparseProject(parseJson(await this.runChecked([\"project\", \"create\", PROJECT_NAME], signal)));\n\t\tif (project.name !== PROJECT_NAME) {\n\t\t\tthrow new BitwardenSecretsManagerStorageError(\n\t\t\t\t\"malformed_provider_data\",\n\t\t\t\t\"Bitwarden Secrets Manager returned the wrong credential project.\",\n\t\t\t);\n\t\t}\n\t\tthis.project = project;\n\t\tconst secretsValue = parseJson(await this.runChecked([\"secret\", \"list\", project.id], signal));\n\t\tif (!Array.isArray(secretsValue) || secretsValue.length > MAX_PROVIDER_ITEMS) {\n\t\t\tthrow new BitwardenSecretsManagerStorageError(\n\t\t\t\t\"malformed_provider_data\",\n\t\t\t\t\"Bitwarden Secrets Manager returned an invalid secret list.\",\n\t\t\t);\n\t\t}\n\t\tconst secrets = secretsValue.map((secret) => parseSecret(secret, project.id));\n\t\tconst keySecrets = secrets.filter((secret) => secret.key === DATA_KEY_NAME);\n\t\tif (keySecrets.length > 1) {\n\t\t\tthrow new BitwardenSecretsManagerStorageError(\n\t\t\t\t\"malformed_provider_data\",\n\t\t\t\t\"Bitwarden Secrets Manager contains duplicate Pi encryption keys.\",\n\t\t\t);\n\t\t}\n\t\tconst nextKey = keySecrets[0] ? parseDataKey(keySecrets[0].value) : undefined;\n\t\tconst nextProfiles = new Map<string, CachedSecretsManagerProfile>();\n\t\ttry {\n\t\t\tfor (const secret of secrets) {\n\t\t\t\tif (!secret.key.startsWith(CREDENTIAL_PROFILE_KEY_PREFIX)) continue;\n\t\t\t\tif (!nextKey) {\n\t\t\t\t\tthrow new BitwardenSecretsManagerStorageError(\n\t\t\t\t\t\t\"malformed_provider_data\",\n\t\t\t\t\t\t\"Bitwarden Secrets Manager profile encryption key is missing.\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst profile = secret.key.slice(CREDENTIAL_PROFILE_KEY_PREFIX.length);\n\t\t\t\ttry {\n\t\t\t\t\tvalidateCredentialProfileName(profile);\n\t\t\t\t} catch {\n\t\t\t\t\tthrow new BitwardenSecretsManagerStorageError(\n\t\t\t\t\t\t\"malformed_provider_data\",\n\t\t\t\t\t\t\"Bitwarden Secrets Manager returned malformed profile data.\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (nextProfiles.has(profile)) {\n\t\t\t\t\tthrow new BitwardenSecretsManagerStorageError(\n\t\t\t\t\t\t\"malformed_provider_data\",\n\t\t\t\t\t\t\"Bitwarden Secrets Manager contains duplicate Pi credential profiles.\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tnextProfiles.set(profile, { id: secret.id, record: decryptProfile(secret.value, profile, nextKey) });\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tfor (const cached of nextProfiles.values()) {\n\t\t\t\tfor (const variable of cached.record.variables) variable.value = \"\";\n\t\t\t}\n\t\t\tnextKey?.fill(0);\n\t\t\tthrow error;\n\t\t}\n\t\tthis.dataKey?.fill(0);\n\t\tthis.dataKey = nextKey;\n\t\tfor (const cached of this.profiles.values()) {\n\t\t\tfor (const variable of cached.record.variables) variable.value = \"\";\n\t\t}\n\t\tthis.profiles.clear();\n\t\tfor (const [profile, cached] of nextProfiles) this.profiles.set(profile, cached);\n\t\tthis.lastSyncAt = Date.now();\n\t}\n\n\tprivate async ensureDataKeyUnderCoordination(signal?: AbortSignal): Promise<Buffer> {\n\t\tif (this.dataKey) return this.dataKey;\n\t\tconst { project } = this.assertConnected();\n\t\tconst generated = randomBytes(32);\n\t\tlet serialized = `${DATA_KEY_PREFIX}${generated.toString(\"base64\")}`;\n\t\ttry {\n\t\t\tconst stored = parseSecret(\n\t\t\t\tparseJson(\n\t\t\t\t\tawait this.runChecked(\n\t\t\t\t\t\t[\"secret\", \"create\", DATA_KEY_NAME, serialized, project.id, \"--note\", DATA_KEY_NOTE],\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t\tproject.id,\n\t\t\t);\n\t\t\tif (stored.key !== DATA_KEY_NAME || stored.value !== serialized) {\n\t\t\t\tthrow new BitwardenSecretsManagerStorageError(\n\t\t\t\t\t\"malformed_provider_data\",\n\t\t\t\t\t\"Bitwarden Secrets Manager returned a malformed profile encryption key.\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.dataKey = Buffer.from(generated);\n\t\t\treturn this.dataKey;\n\t\t} finally {\n\t\t\tgenerated.fill(0);\n\t\t\tserialized = \"\";\n\t\t}\n\t}\n\n\tprivate async withProviderCoordination<T>(signal: AbortSignal | undefined, operation: () => Promise<T>): Promise<T> {\n\t\tsignal?.throwIfAborted();\n\t\ttry {\n\t\t\treturn await withFileLock(\n\t\t\t\tthis.coordinationFile,\n\t\t\t\tasync () => {\n\t\t\t\t\tsignal?.throwIfAborted();\n\t\t\t\t\treturn operation();\n\t\t\t\t},\n\t\t\t\tPROVIDER_COORDINATION_LOCK_OPTIONS,\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tif (error instanceof CredentialStorageError || signal?.aborted) throw error;\n\t\t\tthrow new BitwardenSecretsManagerStorageError(\n\t\t\t\t\"provider_command_failed\",\n\t\t\t\t\"Bitwarden Secrets Manager coordination failed safely.\",\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate async runChecked(args: string[], signal?: AbortSignal): Promise<string> {\n\t\tif (!this.executable || !this.accessToken) {\n\t\t\tthrow new BitwardenSecretsManagerStorageError(\"not_connected\", \"Bitwarden is not connected.\");\n\t\t}\n\t\tconst result = await this.runCommand({\n\t\t\texecutable: this.executable,\n\t\t\targs,\n\t\t\taccessToken: this.accessToken,\n\t\t\t...(signal ? { signal } : {}),\n\t\t});\n\t\tif (\n\t\t\tresult.exitCode !== 0 ||\n\t\t\tresult.aborted === true ||\n\t\t\tresult.timedOut === true ||\n\t\t\tresult.outputLimitExceeded === true\n\t\t) {\n\t\t\tthrow new BitwardenSecretsManagerStorageError(\n\t\t\t\t\"provider_command_failed\",\n\t\t\t\t\"Bitwarden Secrets Manager command failed safely without exposing provider output.\",\n\t\t\t);\n\t\t}\n\t\treturn result.stdout;\n\t}\n}\n"]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface CredentialCliCommandRequest {
|
|
2
|
+
executable: string;
|
|
3
|
+
args: string[];
|
|
4
|
+
input?: string;
|
|
5
|
+
authEnvironment: {
|
|
6
|
+
name: "BWS_ACCESS_TOKEN" | "BW_SESSION";
|
|
7
|
+
value: string;
|
|
8
|
+
};
|
|
9
|
+
omitEnvironmentVariables?: readonly string[];
|
|
10
|
+
signal?: AbortSignal;
|
|
11
|
+
}
|
|
12
|
+
export interface CredentialCliCommandResult {
|
|
13
|
+
exitCode: number;
|
|
14
|
+
stdout: string;
|
|
15
|
+
stderr: string;
|
|
16
|
+
aborted?: boolean;
|
|
17
|
+
timedOut?: boolean;
|
|
18
|
+
outputLimitExceeded?: boolean;
|
|
19
|
+
}
|
|
20
|
+
/** Shared bounded process boundary for Bitwarden provider adapters. */
|
|
21
|
+
export declare function runCredentialCliCommand(request: CredentialCliCommandRequest): Promise<CredentialCliCommandResult>;
|
|
22
|
+
//# sourceMappingURL=credential-cli-command.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credential-cli-command.d.ts","sourceRoot":"","sources":["../../../src/core/secrets/credential-cli-command.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,2BAA2B;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,EAAE;QAAE,IAAI,EAAE,kBAAkB,GAAG,YAAY,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5E,wBAAwB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7C,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB;AAED,MAAM,WAAW,0BAA0B;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAkBD,uEAAuE;AACvE,wBAAsB,uBAAuB,CAC5C,OAAO,EAAE,2BAA2B,GAClC,OAAO,CAAC,0BAA0B,CAAC,CAwDrC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { spawnProcess, waitForChildProcessWithTermination } from "../../utils/child-process.js";
|
|
2
|
+
const MAX_COMMAND_OUTPUT_BYTES = 4 * 1024 * 1024;
|
|
3
|
+
const COMMAND_TIMEOUT_MS = 30_000;
|
|
4
|
+
function appendBounded(current, chunk) {
|
|
5
|
+
const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
6
|
+
if (Buffer.byteLength(current, "utf8") + Buffer.byteLength(text, "utf8") <= MAX_COMMAND_OUTPUT_BYTES) {
|
|
7
|
+
return { value: current + text, exceeded: false };
|
|
8
|
+
}
|
|
9
|
+
return { value: current, exceeded: true };
|
|
10
|
+
}
|
|
11
|
+
function combineAbortSignal(source, controller) {
|
|
12
|
+
if (!source)
|
|
13
|
+
return () => { };
|
|
14
|
+
const abort = () => controller.abort();
|
|
15
|
+
if (source.aborted)
|
|
16
|
+
abort();
|
|
17
|
+
else
|
|
18
|
+
source.addEventListener("abort", abort, { once: true });
|
|
19
|
+
return () => source.removeEventListener("abort", abort);
|
|
20
|
+
}
|
|
21
|
+
/** Shared bounded process boundary for Bitwarden provider adapters. */
|
|
22
|
+
export async function runCredentialCliCommand(request) {
|
|
23
|
+
const controller = new AbortController();
|
|
24
|
+
const detachAbort = combineAbortSignal(request.signal, controller);
|
|
25
|
+
let stdout = "";
|
|
26
|
+
let stderr = "";
|
|
27
|
+
let outputLimitExceeded = false;
|
|
28
|
+
let child;
|
|
29
|
+
try {
|
|
30
|
+
const environment = {
|
|
31
|
+
...process.env,
|
|
32
|
+
[request.authEnvironment.name]: request.authEnvironment.value,
|
|
33
|
+
NO_COLOR: "1",
|
|
34
|
+
};
|
|
35
|
+
for (const name of request.omitEnvironmentVariables ?? [])
|
|
36
|
+
delete environment[name];
|
|
37
|
+
child = spawnProcess(request.executable, request.args, {
|
|
38
|
+
env: environment,
|
|
39
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
40
|
+
windowsHide: true,
|
|
41
|
+
});
|
|
42
|
+
child.stdout?.on("data", (chunk) => {
|
|
43
|
+
const appended = appendBounded(stdout, chunk);
|
|
44
|
+
stdout = appended.value;
|
|
45
|
+
if (appended.exceeded) {
|
|
46
|
+
outputLimitExceeded = true;
|
|
47
|
+
controller.abort();
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
child.stderr?.on("data", (chunk) => {
|
|
51
|
+
const appended = appendBounded(stderr, chunk);
|
|
52
|
+
stderr = appended.value;
|
|
53
|
+
if (appended.exceeded) {
|
|
54
|
+
outputLimitExceeded = true;
|
|
55
|
+
controller.abort();
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
child.stdin?.end(request.input);
|
|
59
|
+
const terminal = await waitForChildProcessWithTermination(child, {
|
|
60
|
+
signal: controller.signal,
|
|
61
|
+
timeoutMs: COMMAND_TIMEOUT_MS,
|
|
62
|
+
killGraceMs: 2_000,
|
|
63
|
+
});
|
|
64
|
+
return {
|
|
65
|
+
exitCode: terminal.code ?? 1,
|
|
66
|
+
stdout,
|
|
67
|
+
stderr,
|
|
68
|
+
...(terminal.reason === "aborted" ? { aborted: true } : {}),
|
|
69
|
+
...(terminal.reason === "timeout" ? { timedOut: true } : {}),
|
|
70
|
+
...(outputLimitExceeded ? { outputLimitExceeded: true } : {}),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return { exitCode: 1, stdout: "", stderr: "" };
|
|
75
|
+
}
|
|
76
|
+
finally {
|
|
77
|
+
detachAbort();
|
|
78
|
+
stdout = "";
|
|
79
|
+
stderr = "";
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=credential-cli-command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credential-cli-command.js","sourceRoot":"","sources":["../../../src/core/secrets/credential-cli-command.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ChildProcess } from \"node:child_process\";\nimport { spawnProcess, waitForChildProcessWithTermination } from \"../../utils/child-process.ts\";\n\nconst MAX_COMMAND_OUTPUT_BYTES = 4 * 1024 * 1024;\nconst COMMAND_TIMEOUT_MS = 30_000;\n\nexport interface CredentialCliCommandRequest {\n\texecutable: string;\n\targs: string[];\n\tinput?: string;\n\tauthEnvironment: { name: \"BWS_ACCESS_TOKEN\" | \"BW_SESSION\"; value: string };\n\tomitEnvironmentVariables?: readonly string[];\n\tsignal?: AbortSignal;\n}\n\nexport interface CredentialCliCommandResult {\n\texitCode: number;\n\tstdout: string;\n\tstderr: string;\n\taborted?: boolean;\n\ttimedOut?: boolean;\n\toutputLimitExceeded?: boolean;\n}\n\nfunction appendBounded(current: string, chunk: Buffer | string): { value: string; exceeded: boolean } {\n\tconst text = typeof chunk === \"string\" ? chunk : chunk.toString(\"utf8\");\n\tif (Buffer.byteLength(current, \"utf8\") + Buffer.byteLength(text, \"utf8\") <= MAX_COMMAND_OUTPUT_BYTES) {\n\t\treturn { value: current + text, exceeded: false };\n\t}\n\treturn { value: current, exceeded: true };\n}\n\nfunction combineAbortSignal(source: AbortSignal | undefined, controller: AbortController): () => void {\n\tif (!source) return () => {};\n\tconst abort = () => controller.abort();\n\tif (source.aborted) abort();\n\telse source.addEventListener(\"abort\", abort, { once: true });\n\treturn () => source.removeEventListener(\"abort\", abort);\n}\n\n/** Shared bounded process boundary for Bitwarden provider adapters. */\nexport async function runCredentialCliCommand(\n\trequest: CredentialCliCommandRequest,\n): Promise<CredentialCliCommandResult> {\n\tconst controller = new AbortController();\n\tconst detachAbort = combineAbortSignal(request.signal, controller);\n\tlet stdout = \"\";\n\tlet stderr = \"\";\n\tlet outputLimitExceeded = false;\n\tlet child: ChildProcess;\n\ttry {\n\t\tconst environment = {\n\t\t\t...process.env,\n\t\t\t[request.authEnvironment.name]: request.authEnvironment.value,\n\t\t\tNO_COLOR: \"1\",\n\t\t};\n\t\tfor (const name of request.omitEnvironmentVariables ?? []) delete environment[name];\n\t\tchild = spawnProcess(request.executable, request.args, {\n\t\t\tenv: environment,\n\t\t\tstdio: [\"pipe\", \"pipe\", \"pipe\"],\n\t\t\twindowsHide: true,\n\t\t});\n\t\tchild.stdout?.on(\"data\", (chunk: Buffer | string) => {\n\t\t\tconst appended = appendBounded(stdout, chunk);\n\t\t\tstdout = appended.value;\n\t\t\tif (appended.exceeded) {\n\t\t\t\toutputLimitExceeded = true;\n\t\t\t\tcontroller.abort();\n\t\t\t}\n\t\t});\n\t\tchild.stderr?.on(\"data\", (chunk: Buffer | string) => {\n\t\t\tconst appended = appendBounded(stderr, chunk);\n\t\t\tstderr = appended.value;\n\t\t\tif (appended.exceeded) {\n\t\t\t\toutputLimitExceeded = true;\n\t\t\t\tcontroller.abort();\n\t\t\t}\n\t\t});\n\t\tchild.stdin?.end(request.input);\n\t\tconst terminal = await waitForChildProcessWithTermination(child, {\n\t\t\tsignal: controller.signal,\n\t\t\ttimeoutMs: COMMAND_TIMEOUT_MS,\n\t\t\tkillGraceMs: 2_000,\n\t\t});\n\t\treturn {\n\t\t\texitCode: terminal.code ?? 1,\n\t\t\tstdout,\n\t\t\tstderr,\n\t\t\t...(terminal.reason === \"aborted\" ? { aborted: true } : {}),\n\t\t\t...(terminal.reason === \"timeout\" ? { timedOut: true } : {}),\n\t\t\t...(outputLimitExceeded ? { outputLimitExceeded: true } : {}),\n\t\t};\n\t} catch {\n\t\treturn { exitCode: 1, stdout: \"\", stderr: \"\" };\n\t} finally {\n\t\tdetachAbort();\n\t\tstdout = \"\";\n\t\tstderr = \"\";\n\t}\n}\n"]}
|
|
@@ -9,5 +9,5 @@ export declare function isProtectedCredentialPath(rawPath: string, cwd: string,
|
|
|
9
9
|
/** Stable model-facing refusal for direct inspection/mutation of credential material. */
|
|
10
10
|
export declare function credentialToolBlockReason(toolName: string, args: unknown, cwd: string, boundary?: CredentialExposureBoundary): string | undefined;
|
|
11
11
|
/** Apply the same path refusal and output redaction to foreground, extension, scout, and lane tools. */
|
|
12
|
-
export declare function wrapToolWithCredentialExposureGuard<TParameters extends TSchema, TDetails>(tool: AgentTool<TParameters, TDetails>,
|
|
12
|
+
export declare function wrapToolWithCredentialExposureGuard<TParameters extends TSchema, TDetails>(tool: AgentTool<TParameters, TDetails>, cwd: string, boundary?: CredentialExposureBoundary): AgentTool<TParameters, TDetails>;
|
|
13
13
|
//# sourceMappingURL=credential-exposure-guard.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credential-exposure-guard.d.ts","sourceRoot":"","sources":["../../../src/core/secrets/credential-exposure-guard.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAiD,MAAM,2BAA2B,CAAC;AAC1G,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAgCvC,MAAM,WAAW,0BAA0B;IAC1C,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1C,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,oBAAoB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACzC;AAWD,wBAAgB,yBAAyB,CACxC,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,0BAA0B,GACnC,OAAO,CAgCT;AAyHD,yFAAyF;AACzF,wBAAgB,yBAAyB,CACxC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,OAAO,EACb,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,0BAA0B,GACnC,MAAM,GAAG,SAAS,CAwDpB;AAuCD,wGAAwG;AACxG,wBAAgB,mCAAmC,CAAC,WAAW,SAAS,OAAO,EAAE,QAAQ,EACxF,IAAI,EAAE,SAAS,CAAC,WAAW,EAAE,QAAQ,CAAC,EACtC,
|
|
1
|
+
{"version":3,"file":"credential-exposure-guard.d.ts","sourceRoot":"","sources":["../../../src/core/secrets/credential-exposure-guard.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAiD,MAAM,2BAA2B,CAAC;AAC1G,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAgCvC,MAAM,WAAW,0BAA0B;IAC1C,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1C,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,oBAAoB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACzC;AAWD,wBAAgB,yBAAyB,CACxC,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,0BAA0B,GACnC,OAAO,CAgCT;AAyHD,yFAAyF;AACzF,wBAAgB,yBAAyB,CACxC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,OAAO,EACb,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,0BAA0B,GACnC,MAAM,GAAG,SAAS,CAwDpB;AAuCD,wGAAwG;AACxG,wBAAgB,mCAAmC,CAAC,WAAW,SAAS,OAAO,EAAE,QAAQ,EACxF,IAAI,EAAE,SAAS,CAAC,WAAW,EAAE,QAAQ,CAAC,EACtC,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,0BAA0B,GACnC,SAAS,CAAC,WAAW,EAAE,QAAQ,CAAC,CAkClC"}
|
|
@@ -275,10 +275,14 @@ function redactStructuredDetails(value, redact, budget, depth = 0) {
|
|
|
275
275
|
return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, redactStructuredDetails(entry, redact, budget, depth + 1)]));
|
|
276
276
|
}
|
|
277
277
|
/** Apply the same path refusal and output redaction to foreground, extension, scout, and lane tools. */
|
|
278
|
-
export function wrapToolWithCredentialExposureGuard(tool,
|
|
278
|
+
export function wrapToolWithCredentialExposureGuard(tool, cwd, boundary) {
|
|
279
279
|
return {
|
|
280
280
|
...tool,
|
|
281
281
|
async execute(toolCallId, params, signal, onUpdate) {
|
|
282
|
+
const blockReason = credentialToolBlockReason(tool.name, params, cwd, boundary);
|
|
283
|
+
if (blockReason) {
|
|
284
|
+
throw new AgentToolExecutionError(blockReason, "credential_access_blocked", "credential-access-blocked", "tool_failure");
|
|
285
|
+
}
|
|
282
286
|
const safeUpdate = onUpdate
|
|
283
287
|
? (partial) => {
|
|
284
288
|
onUpdate(redactResult(partial, boundary));
|