crosscheck-cli 0.0.44 → 0.0.46

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.
@@ -0,0 +1,254 @@
1
+ /**
2
+ * The `op` CLI runner — the only place this backend ever executes anything.
3
+ * Security checkpoint (see docs/onepassword-security.md): argv construction,
4
+ * environment policy, timeouts/cleanup, output caps, and the error taxonomy
5
+ * all live here.
6
+ *
7
+ * Hard rules:
8
+ * - spawn() only, never a shell. Fixed argv shape, no user-controlled flags.
9
+ * - The reference is re-validated here even though config.ts already
10
+ * validated it on load — defense in depth, not redundancy for its own
11
+ * sake, since this is the last checkpoint before anything reaches argv.
12
+ * - Resolved secret values, tokens, and full op:// references never reach
13
+ * a log, an error message, or telemetry. Errors carry category +
14
+ * remediation + provider-agnostic detail only.
15
+ * - Fail closed: any ambiguity (unparseable version, unmatched stderr,
16
+ * unexpected exit) becomes a generic "unknown" error, never a pass.
17
+ */
18
+ import { spawn } from "node:child_process";
19
+ import { parseOpReference } from "./reference.js";
20
+ const DEFAULT_OP_BIN = "op";
21
+ // Best-effort minimum — 1Password CLI v2 added desktop-app integration and
22
+ // service-account support well before this version. NEEDS EMPIRICAL
23
+ // VERIFICATION against a real `op` install before relying on it as a hard
24
+ // gate in production (see docs/onepassword-security.md's open items).
25
+ export const MIN_OP_VERSION = "2.18.0";
26
+ const DESKTOP_TIMEOUT_MS = 120_000; // interactive: biometric/hardware-key prompts can't be rushed
27
+ const SERVICE_ACCOUNT_TIMEOUT_MS = 10_000; // non-interactive: a hang means misconfiguration
28
+ const VERSION_CHECK_TIMEOUT_MS = 10_000;
29
+ const GRACE_MS = 2_000; // SIGTERM -> grace -> SIGKILL
30
+ const MAX_OUTPUT_BYTES = 64 * 1024; // generous for a single secret value or short error text
31
+ // Env vars `op` itself recognizes for service-account/Connect auth. Stripped
32
+ // from a desktop-mode child regardless of what the parent process happens to
33
+ // have set — a stray token in someone's shell must never silently switch
34
+ // which identity the child authenticates as.
35
+ const OP_AUTH_ENV_VARS = ["OP_SERVICE_ACCOUNT_TOKEN", "OP_CONNECT_HOST", "OP_CONNECT_TOKEN"];
36
+ function runOpProcess(opBin, args, opts) {
37
+ return new Promise((resolve, reject) => {
38
+ let stdout = "";
39
+ let stderr = "";
40
+ let stdoutBytes = 0;
41
+ let stderrBytes = 0;
42
+ let outputTooLarge = false;
43
+ let timedOut = false;
44
+ let killedForOverflow = false;
45
+ let graceTimer;
46
+ let settled = false;
47
+ const child = spawn(opBin, args, {
48
+ env: opts.env,
49
+ stdio: ["ignore", "pipe", "pipe"],
50
+ shell: false,
51
+ });
52
+ const timeoutTimer = setTimeout(() => {
53
+ timedOut = true;
54
+ child.kill("SIGTERM");
55
+ graceTimer = setTimeout(() => {
56
+ try {
57
+ child.kill("SIGKILL");
58
+ }
59
+ catch {
60
+ /* already gone */
61
+ }
62
+ }, GRACE_MS);
63
+ }, opts.timeoutMs);
64
+ function checkOverflow() {
65
+ if (outputTooLarge || killedForOverflow)
66
+ return;
67
+ if (stdoutBytes + stderrBytes > MAX_OUTPUT_BYTES) {
68
+ outputTooLarge = true;
69
+ killedForOverflow = true;
70
+ child.kill("SIGTERM");
71
+ }
72
+ }
73
+ child.stdout.on("data", (chunk) => {
74
+ stdoutBytes += chunk.length;
75
+ if (stdoutBytes <= MAX_OUTPUT_BYTES)
76
+ stdout += chunk.toString("utf8");
77
+ checkOverflow();
78
+ });
79
+ child.stderr.on("data", (chunk) => {
80
+ stderrBytes += chunk.length;
81
+ if (stderrBytes <= MAX_OUTPUT_BYTES)
82
+ stderr += chunk.toString("utf8");
83
+ checkOverflow();
84
+ });
85
+ child.on("error", (err) => {
86
+ if (settled)
87
+ return;
88
+ settled = true;
89
+ clearTimeout(timeoutTimer);
90
+ if (graceTimer)
91
+ clearTimeout(graceTimer);
92
+ reject(err);
93
+ });
94
+ child.on("close", (code, signal) => {
95
+ if (settled)
96
+ return;
97
+ settled = true;
98
+ clearTimeout(timeoutTimer);
99
+ if (graceTimer)
100
+ clearTimeout(graceTimer);
101
+ resolve({ code, signal, stdout, stderr, timedOut, outputTooLarge });
102
+ });
103
+ });
104
+ }
105
+ function parseVersion(v) {
106
+ const m = /^(\d+)\.(\d+)\.(\d+)/.exec(v.trim());
107
+ if (!m)
108
+ return null;
109
+ return [Number(m[1]), Number(m[2]), Number(m[3])];
110
+ }
111
+ function isVersionAtLeast(actual, minimum) {
112
+ const a = parseVersion(actual);
113
+ const b = parseVersion(minimum);
114
+ if (!a || !b)
115
+ return false; // fail closed on an unparseable version string
116
+ for (let i = 0; i < 3; i += 1) {
117
+ if (a[i] > b[i])
118
+ return true;
119
+ if (a[i] < b[i])
120
+ return false;
121
+ }
122
+ return true; // equal
123
+ }
124
+ export async function checkOpVersion(opts = {}) {
125
+ const opBin = opts.opBin ?? DEFAULT_OP_BIN;
126
+ let result;
127
+ try {
128
+ result = await runOpProcess(opBin, ["--version"], {
129
+ env: process.env,
130
+ timeoutMs: VERSION_CHECK_TIMEOUT_MS,
131
+ });
132
+ }
133
+ catch (e) {
134
+ const err = e;
135
+ if (err.code === "ENOENT") {
136
+ return { ok: false, error: "not-installed", detail: `\`${opBin}\` was not found on PATH` };
137
+ }
138
+ return { ok: false, error: "unknown", detail: "could not determine the installed op version" };
139
+ }
140
+ if (result.code !== 0 || result.timedOut) {
141
+ return { ok: false, error: "unknown", detail: "`op --version` did not exit cleanly" };
142
+ }
143
+ const version = result.stdout.trim();
144
+ if (!isVersionAtLeast(version, MIN_OP_VERSION)) {
145
+ return {
146
+ ok: false,
147
+ error: "wrong-version",
148
+ detail: `op ${version || "(unknown)"} is older than the minimum supported ${MIN_OP_VERSION} — upgrade the 1Password CLI`,
149
+ };
150
+ }
151
+ return { ok: true, version };
152
+ }
153
+ /** Env vars 1Password's desktop-IPC auth needs vary by platform and haven't
154
+ * been empirically verified across all of them (see docs/onepassword-
155
+ * security.md). Until that verification lands, desktop mode uses
156
+ * inherited-env-minus-auth-vars rather than a tighter allowlist — safe
157
+ * (auth vars are still stripped) but not yet as narrow as the target. */
158
+ function buildChildEnv(opts) {
159
+ const env = { ...process.env };
160
+ for (const key of OP_AUTH_ENV_VARS)
161
+ delete env[key];
162
+ if (opts.authMode === "service-account") {
163
+ env["OP_SERVICE_ACCOUNT_TOKEN"] = opts.serviceAccountToken;
164
+ }
165
+ return env;
166
+ }
167
+ function classifyOpFailure(stderr, authMode) {
168
+ const s = stderr.toLowerCase();
169
+ if (s.includes("not currently signed in") || s.includes("not signed in") || s.includes("sign in")) {
170
+ return {
171
+ ok: false,
172
+ error: "not-signed-in",
173
+ detail: authMode === "desktop"
174
+ ? "unlock prompt unanswered or desktop app unreachable — if running headless, configure service-account mode"
175
+ : "check that the service-account token is valid and not expired",
176
+ };
177
+ }
178
+ if (s.includes("access denied") || s.includes("forbidden") || s.includes("permission")) {
179
+ return {
180
+ ok: false,
181
+ error: "access-denied",
182
+ detail: "the authenticated identity does not have access to this item",
183
+ };
184
+ }
185
+ if (s.includes("could not find") ||
186
+ s.includes("not found") ||
187
+ s.includes("no such") ||
188
+ s.includes("isn't a vault") ||
189
+ s.includes("isn't an item")) {
190
+ return {
191
+ ok: false,
192
+ error: "item-or-field-missing",
193
+ detail: "the referenced vault, item, or field was not found",
194
+ };
195
+ }
196
+ // Conservative catch-all: never surface raw stderr — it could echo back
197
+ // parts of the reference, item names, or unrelated system paths.
198
+ return {
199
+ ok: false,
200
+ error: "unknown",
201
+ detail: "op exited with an error — run `op read <ref>` manually to see the full message",
202
+ };
203
+ }
204
+ /**
205
+ * Read one secret value from 1Password. Re-validates `refInput` itself
206
+ * (never trusts a caller's prior validation), never falls back to any other
207
+ * source on failure, and never lets a resolved value, token, or reference
208
+ * reach the returned error on the failure path.
209
+ */
210
+ export async function readOpSecret(refInput, opts) {
211
+ const parsed = parseOpReference(refInput);
212
+ if (!parsed.ok) {
213
+ return { ok: false, error: "malformed-reference", detail: parsed.detail };
214
+ }
215
+ if (opts.authMode === "service-account" && !opts.serviceAccountToken) {
216
+ return { ok: false, error: "token-missing", detail: "service-account mode requires a token" };
217
+ }
218
+ const opBin = opts.opBin ?? DEFAULT_OP_BIN;
219
+ const versionCheck = await checkOpVersion({ opBin });
220
+ if (!versionCheck.ok)
221
+ return versionCheck;
222
+ const env = buildChildEnv(opts);
223
+ const timeoutMs = opts.authMode === "desktop" ? DESKTOP_TIMEOUT_MS : SERVICE_ACCOUNT_TIMEOUT_MS;
224
+ let result;
225
+ try {
226
+ result = await runOpProcess(opBin, ["read", parsed.ref], { env, timeoutMs });
227
+ }
228
+ catch (e) {
229
+ const err = e;
230
+ if (err.code === "ENOENT") {
231
+ return { ok: false, error: "not-installed", detail: `\`${opBin}\` was not found on PATH` };
232
+ }
233
+ return { ok: false, error: "unknown", detail: "failed to launch the op process" };
234
+ }
235
+ if (result.outputTooLarge) {
236
+ return {
237
+ ok: false,
238
+ error: "output-too-large",
239
+ detail: `op output exceeded ${MAX_OUTPUT_BYTES} bytes`,
240
+ };
241
+ }
242
+ if (result.timedOut) {
243
+ return { ok: false, error: "timeout", detail: `op did not respond within ${timeoutMs}ms` };
244
+ }
245
+ if (result.code !== 0) {
246
+ return classifyOpFailure(result.stderr, opts.authMode);
247
+ }
248
+ const value = result.stdout.replace(/\r?\n$/, "");
249
+ if (value.length === 0) {
250
+ return { ok: false, error: "empty-value", detail: "op returned an empty value" };
251
+ }
252
+ return { ok: true, value };
253
+ }
254
+ //# sourceMappingURL=op.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"op.js","sourceRoot":"","sources":["../../src/onepassword/op.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAGlD,MAAM,cAAc,GAAG,IAAI,CAAC;AAE5B,2EAA2E;AAC3E,oEAAoE;AACpE,0EAA0E;AAC1E,sEAAsE;AACtE,MAAM,CAAC,MAAM,cAAc,GAAG,QAAQ,CAAC;AAEvC,MAAM,kBAAkB,GAAG,OAAO,CAAC,CAAC,8DAA8D;AAClG,MAAM,0BAA0B,GAAG,MAAM,CAAC,CAAC,iDAAiD;AAC5F,MAAM,wBAAwB,GAAG,MAAM,CAAC;AACxC,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,8BAA8B;AACtD,MAAM,gBAAgB,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,yDAAyD;AAE7F,6EAA6E;AAC7E,6EAA6E;AAC7E,yEAAyE;AACzE,6CAA6C;AAC7C,MAAM,gBAAgB,GAAG,CAAC,0BAA0B,EAAE,iBAAiB,EAAE,kBAAkB,CAAC,CAAC;AAyC7F,SAAS,YAAY,CACnB,KAAa,EACb,IAAc,EACd,IAAmD;IAEnD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,WAAW,GAAG,CAAC,CAAC;QACpB,IAAI,WAAW,GAAG,CAAC,CAAC;QACpB,IAAI,cAAc,GAAG,KAAK,CAAC;QAC3B,IAAI,QAAQ,GAAG,KAAK,CAAC;QACrB,IAAI,iBAAiB,GAAG,KAAK,CAAC;QAC9B,IAAI,UAAsC,CAAC;QAC3C,IAAI,OAAO,GAAG,KAAK,CAAC;QAEpB,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE;YAC/B,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;YACjC,KAAK,EAAE,KAAK;SACb,CAAC,CAAC;QAEH,MAAM,YAAY,GAAG,UAAU,CAAC,GAAG,EAAE;YACnC,QAAQ,GAAG,IAAI,CAAC;YAChB,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACtB,UAAU,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC3B,IAAI,CAAC;oBACH,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;gBACxB,CAAC;gBAAC,MAAM,CAAC;oBACP,kBAAkB;gBACpB,CAAC;YACH,CAAC,EAAE,QAAQ,CAAC,CAAC;QACf,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QAEnB,SAAS,aAAa;YACpB,IAAI,cAAc,IAAI,iBAAiB;gBAAE,OAAO;YAChD,IAAI,WAAW,GAAG,WAAW,GAAG,gBAAgB,EAAE,CAAC;gBACjD,cAAc,GAAG,IAAI,CAAC;gBACtB,iBAAiB,GAAG,IAAI,CAAC;gBACzB,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACxB,CAAC;QACH,CAAC;QAED,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YACxC,WAAW,IAAI,KAAK,CAAC,MAAM,CAAC;YAC5B,IAAI,WAAW,IAAI,gBAAgB;gBAAE,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YACtE,aAAa,EAAE,CAAC;QAClB,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YACxC,WAAW,IAAI,KAAK,CAAC,MAAM,CAAC;YAC5B,IAAI,WAAW,IAAI,gBAAgB;gBAAE,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YACtE,aAAa,EAAE,CAAC;QAClB,CAAC,CAAC,CAAC;QAEH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACxB,IAAI,OAAO;gBAAE,OAAO;YACpB,OAAO,GAAG,IAAI,CAAC;YACf,YAAY,CAAC,YAAY,CAAC,CAAC;YAC3B,IAAI,UAAU;gBAAE,YAAY,CAAC,UAAU,CAAC,CAAC;YACzC,MAAM,CAAC,GAAG,CAAC,CAAC;QACd,CAAC,CAAC,CAAC;QAEH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE;YACjC,IAAI,OAAO;gBAAE,OAAO;YACpB,OAAO,GAAG,IAAI,CAAC;YACf,YAAY,CAAC,YAAY,CAAC,CAAC;YAC3B,IAAI,UAAU;gBAAE,YAAY,CAAC,UAAU,CAAC,CAAC;YACzC,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,EAAE,CAAC,CAAC;QACtE,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,YAAY,CAAC,CAAS;IAC7B,MAAM,CAAC,GAAG,sBAAsB,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAChD,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACpB,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACpD,CAAC;AAED,SAAS,gBAAgB,CAAC,MAAc,EAAE,OAAe;IACvD,MAAM,CAAC,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IAC/B,MAAM,CAAC,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;IAChC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC,CAAC,+CAA+C;IAC3E,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9B,IAAI,CAAC,CAAC,CAAC,CAAE,GAAG,CAAC,CAAC,CAAC,CAAE;YAAE,OAAO,IAAI,CAAC;QAC/B,IAAI,CAAC,CAAC,CAAC,CAAE,GAAG,CAAC,CAAC,CAAC,CAAE;YAAE,OAAO,KAAK,CAAC;IAClC,CAAC;IACD,OAAO,IAAI,CAAC,CAAC,QAAQ;AACvB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,OAA2B,EAAE;IAChE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,cAAc,CAAC;IAC3C,IAAI,MAAsB,CAAC;IAC3B,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,YAAY,CAAC,KAAK,EAAE,CAAC,WAAW,CAAC,EAAE;YAChD,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,SAAS,EAAE,wBAAwB;SACpC,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,GAAG,GAAG,CAA0B,CAAC;QACvC,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC1B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE,KAAK,KAAK,0BAA0B,EAAE,CAAC;QAC7F,CAAC;QACD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,8CAA8C,EAAE,CAAC;IACjG,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACzC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,qCAAqC,EAAE,CAAC;IACxF,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IACrC,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,cAAc,CAAC,EAAE,CAAC;QAC/C,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,eAAe;YACtB,MAAM,EAAE,MAAM,OAAO,IAAI,WAAW,wCAAwC,cAAc,8BAA8B;SACzH,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AAC/B,CAAC;AAED;;;;0EAI0E;AAC1E,SAAS,aAAa,CAAC,IAAqB;IAC1C,MAAM,GAAG,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAC/B,KAAK,MAAM,GAAG,IAAI,gBAAgB;QAAE,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC;IACpD,IAAI,IAAI,CAAC,QAAQ,KAAK,iBAAiB,EAAE,CAAC;QACxC,GAAG,CAAC,0BAA0B,CAAC,GAAG,IAAI,CAAC,mBAAoB,CAAC;IAC9D,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,iBAAiB,CAAC,MAAc,EAAE,QAAkB;IAC3D,MAAM,CAAC,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC;IAC/B,IAAI,CAAC,CAAC,QAAQ,CAAC,yBAAyB,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QAClG,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,eAAe;YACtB,MAAM,EACJ,QAAQ,KAAK,SAAS;gBACpB,CAAC,CAAC,2GAA2G;gBAC7G,CAAC,CAAC,+DAA+D;SACtE,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,CAAC,QAAQ,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;QACvF,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,eAAe;YACtB,MAAM,EAAE,8DAA8D;SACvE,CAAC;IACJ,CAAC;IACD,IACE,CAAC,CAAC,QAAQ,CAAC,gBAAgB,CAAC;QAC5B,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC;QACvB,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC;QACrB,CAAC,CAAC,QAAQ,CAAC,eAAe,CAAC;QAC3B,CAAC,CAAC,QAAQ,CAAC,eAAe,CAAC,EAC3B,CAAC;QACD,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,uBAAuB;YAC9B,MAAM,EAAE,oDAAoD;SAC7D,CAAC;IACJ,CAAC;IACD,wEAAwE;IACxE,iEAAiE;IACjE,OAAO;QACL,EAAE,EAAE,KAAK;QACT,KAAK,EAAE,SAAS;QAChB,MAAM,EAAE,gFAAgF;KACzF,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,QAAgB,EAAE,IAAqB;IACxE,MAAM,MAAM,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAC1C,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QACf,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,qBAAqB,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;IAC5E,CAAC;IACD,IAAI,IAAI,CAAC,QAAQ,KAAK,iBAAiB,IAAI,CAAC,IAAI,CAAC,mBAAmB,EAAE,CAAC;QACrE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE,uCAAuC,EAAE,CAAC;IAChG,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,cAAc,CAAC;IAC3C,MAAM,YAAY,GAAG,MAAM,cAAc,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;IACrD,IAAI,CAAC,YAAY,CAAC,EAAE;QAAE,OAAO,YAAY,CAAC;IAE1C,MAAM,GAAG,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IAChC,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,0BAA0B,CAAC;IAEhG,IAAI,MAAsB,CAAC;IAC3B,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,YAAY,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC,CAAC;IAC/E,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,GAAG,GAAG,CAA0B,CAAC;QACvC,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC1B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE,KAAK,KAAK,0BAA0B,EAAE,CAAC;QAC7F,CAAC;QACD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,iCAAiC,EAAE,CAAC;IACpF,CAAC;IAED,IAAI,MAAM,CAAC,cAAc,EAAE,CAAC;QAC1B,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,kBAAkB;YACzB,MAAM,EAAE,sBAAsB,gBAAgB,QAAQ;SACvD,CAAC;IACJ,CAAC;IACD,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACpB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,6BAA6B,SAAS,IAAI,EAAE,CAAC;IAC7F,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,iBAAiB,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IACzD,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IAClD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,4BAA4B,EAAE,CAAC;IACnF,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Strict parser/validator for 1Password secret references (`op://vault/item/field`).
3
+ *
4
+ * v1 supports only the plain 3-segment form — no optional `section` segment.
5
+ * Keeping the grammar minimal keeps this file (and its test surface) small,
6
+ * which matters because it's the first of two defense-in-depth checkpoints
7
+ * against CWE-78: op.ts (PR 2) re-validates immediately before spawning `op`,
8
+ * but rejecting anything that isn't a clean `op://...` string here means a
9
+ * malformed/hostile config value never gets far enough to be a runner
10
+ * concern. Anything not starting with the literal "op://" prefix also can't
11
+ * be mistaken for a flag by `op`'s own argument parser, since it's always
12
+ * passed as a single argv element via execFile — never a shell.
13
+ */
14
+ const OP_URI_PREFIX = "op://";
15
+ const MAX_REF_LENGTH = 512;
16
+ // Conservative allowlist per segment. Real op:// references are plain
17
+ // vault/item/field names — never shell metacharacters or control
18
+ // characters. Tighten or loosen only against a verified real `op` binary
19
+ // (see PR 2's empirical env/argv matrix), not by inspection alone.
20
+ const SEGMENT_CHARS = /^[A-Za-z0-9 _.'-]+$/;
21
+ export function parseOpReference(input) {
22
+ if (typeof input !== "string" || input.length === 0) {
23
+ return { ok: false, error: "empty", detail: "reference is empty" };
24
+ }
25
+ if (input.length > MAX_REF_LENGTH) {
26
+ return {
27
+ ok: false,
28
+ error: "too-long",
29
+ detail: `reference exceeds ${MAX_REF_LENGTH} characters`,
30
+ };
31
+ }
32
+ // eslint-disable-next-line no-control-regex
33
+ if (/[\x00-\x1f\x7f]/.test(input)) {
34
+ return {
35
+ ok: false,
36
+ error: "control-characters",
37
+ detail: "reference contains control characters",
38
+ };
39
+ }
40
+ if (input.includes("?") || input.includes("#")) {
41
+ return {
42
+ ok: false,
43
+ error: "query-or-fragment",
44
+ detail: "reference must not contain '?' or '#'",
45
+ };
46
+ }
47
+ if (!input.startsWith(OP_URI_PREFIX)) {
48
+ return {
49
+ ok: false,
50
+ error: "missing-prefix",
51
+ detail: `reference must start with "${OP_URI_PREFIX}"`,
52
+ };
53
+ }
54
+ const rest = input.slice(OP_URI_PREFIX.length);
55
+ const segments = rest.split("/");
56
+ if (segments.length !== 3) {
57
+ return {
58
+ ok: false,
59
+ error: "wrong-segment-count",
60
+ detail: `expected exactly 3 segments (vault/item/field), got ${segments.length}`,
61
+ };
62
+ }
63
+ const [vault, item, field] = segments;
64
+ for (const seg of [vault, item, field]) {
65
+ if (seg.length === 0) {
66
+ return {
67
+ ok: false,
68
+ error: "empty-segment",
69
+ detail: "vault/item/field segments must not be empty",
70
+ };
71
+ }
72
+ if (!SEGMENT_CHARS.test(seg)) {
73
+ return {
74
+ ok: false,
75
+ error: "invalid-characters",
76
+ detail: "segments may only contain letters, digits, spaces, and . _ - '",
77
+ };
78
+ }
79
+ }
80
+ return { ok: true, ref: input, vault, item, field };
81
+ }
82
+ //# sourceMappingURL=reference.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reference.js","sourceRoot":"","sources":["../../src/onepassword/reference.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,MAAM,aAAa,GAAG,OAAO,CAAC;AAC9B,MAAM,cAAc,GAAG,GAAG,CAAC;AAE3B,sEAAsE;AACtE,iEAAiE;AACjE,yEAAyE;AACzE,mEAAmE;AACnE,MAAM,aAAa,GAAG,qBAAqB,CAAC;AAgB5C,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,oBAAoB,EAAE,CAAC;IACrE,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,GAAG,cAAc,EAAE,CAAC;QAClC,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,UAAU;YACjB,MAAM,EAAE,qBAAqB,cAAc,aAAa;SACzD,CAAC;IACJ,CAAC;IACD,4CAA4C;IAC5C,IAAI,iBAAiB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAClC,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,oBAAoB;YAC3B,MAAM,EAAE,uCAAuC;SAChD,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC/C,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,mBAAmB;YAC1B,MAAM,EAAE,uCAAuC;SAChD,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QACrC,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,gBAAgB;YACvB,MAAM,EAAE,8BAA8B,aAAa,GAAG;SACvD,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;IAC/C,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,qBAAqB;YAC5B,MAAM,EAAE,uDAAuD,QAAQ,CAAC,MAAM,EAAE;SACjF,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,GAAG,QAAoC,CAAC;IAClE,KAAK,MAAM,GAAG,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC;QACvC,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACrB,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,KAAK,EAAE,eAAe;gBACtB,MAAM,EAAE,6CAA6C;aACtD,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YAC7B,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,KAAK,EAAE,oBAAoB;gBAC3B,MAAM,EAAE,gEAAgE;aACzE,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,KAAoB,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AACrE,CAAC"}
@@ -0,0 +1,150 @@
1
+ /**
2
+ * `crosscheck keys setup --backend 1password` — validates the config end to
3
+ * end BEFORE ever persisting the backend preference. Never writes anything
4
+ * to 1Password (the backend is read-only); a "test read" only ever proves
5
+ * connectivity — the value is discarded immediately, never printed, never
6
+ * stored.
7
+ */
8
+ import { loadOnePasswordConfig, writeOnePasswordConfig, configFilePath } from "./config.js";
9
+ import { checkOpVersion, readOpSecret, MIN_OP_VERSION } from "./op.js";
10
+ import { captureOnePasswordConfigViaBrowser } from "./browser-flow.js";
11
+ import { enableOnePasswordBackend } from "../keychain.js";
12
+ import { SUPPORTED_PROVIDERS } from "../keys-browser-flow.js";
13
+ /**
14
+ * Validate config → `op` version → test-read every mapped provider.
15
+ * Discloses provider NAMES only, never a full op:// reference — matching
16
+ * the same disclosure discipline as op.ts's error taxonomy. Never touches
17
+ * the persisted backend preference; the caller only does that after seeing
18
+ * `{ ok: true }`.
19
+ *
20
+ * "Success" here means at least one mapped provider resolved — not that
21
+ * every one did. A single bad item/field name is a per-provider config
22
+ * typo the user can fix later; it shouldn't block enabling the backend for
23
+ * every OTHER provider that already works. A systemic problem (op missing,
24
+ * wrong version, or every single provider failing — usually a signed-out
25
+ * or invalid-token case) DOES block, since persisting a backend where
26
+ * nothing works at all isn't useful and would just defer the failure to
27
+ * the next `crosscheck serve`.
28
+ */
29
+ export async function validateOnePasswordSetup() {
30
+ const cfg = loadOnePasswordConfig();
31
+ if (!cfg.ok) {
32
+ return {
33
+ ok: false,
34
+ stage: "config",
35
+ detail: `${cfg.error}: ${cfg.detail} (expected at ${configFilePath()})`,
36
+ };
37
+ }
38
+ const versionCheck = await checkOpVersion();
39
+ if (!versionCheck.ok) {
40
+ return { ok: false, stage: "op-version", detail: versionCheck.detail };
41
+ }
42
+ const providerNames = Object.keys(cfg.config.mappings).sort();
43
+ const results = [];
44
+ for (const provider of providerNames) {
45
+ const ref = cfg.config.mappings[provider];
46
+ const result = await readOpSecret(ref, {
47
+ authMode: cfg.config.authMode,
48
+ serviceAccountToken: cfg.config.authMode === "service-account"
49
+ ? process.env.OP_SERVICE_ACCOUNT_TOKEN
50
+ : undefined,
51
+ });
52
+ results.push(result.ok
53
+ ? { provider, ok: true }
54
+ : { provider, ok: false, error: `${result.error}: ${result.detail}` });
55
+ }
56
+ if (providerNames.length === 0) {
57
+ return {
58
+ ok: false,
59
+ stage: "no-providers-resolved",
60
+ detail: "no providers are mapped in the config — add at least one before enabling",
61
+ providers: results,
62
+ };
63
+ }
64
+ if (!results.some((r) => r.ok)) {
65
+ return {
66
+ ok: false,
67
+ stage: "no-providers-resolved",
68
+ detail: "no configured provider resolved successfully",
69
+ providers: results,
70
+ };
71
+ }
72
+ return { ok: true, authMode: cfg.config.authMode, providers: results };
73
+ }
74
+ function printProviderResults(results) {
75
+ for (const p of results) {
76
+ process.stdout.write(p.ok ? ` ✓ ${p.provider}\n` : ` ✗ ${p.provider}: ${p.error}\n`);
77
+ }
78
+ }
79
+ /**
80
+ * Orchestrates `crosscheck keys setup --backend 1password`: by default,
81
+ * opens a browser wizard to collect authMode + per-provider Vault/Item/Field
82
+ * (pre-filled with whatever's already configured, so re-running to add one
83
+ * more provider doesn't lose the rest) and writes the config — no one has
84
+ * to hand-edit ~/.crosscheck/1password.json. Pass `{ useFile: true }` (the
85
+ * CLI's `--file` flag) to skip the browser and just validate a config
86
+ * that's already been placed there some other way (dotfiles, CI
87
+ * provisioning, etc.). Either way, ends the same: validate, then (only on
88
+ * success) persist the preference and report what's usable.
89
+ */
90
+ export async function runOnePasswordKeysSetup(opts = {}) {
91
+ if (!opts.useFile) {
92
+ const existingLoad = loadOnePasswordConfig();
93
+ const existing = existingLoad.ok ? existingLoad.config.mappings : undefined;
94
+ const existingAuthMode = existingLoad.ok ? existingLoad.config.authMode : undefined;
95
+ process.stdout.write("\nLet's set up 1Password in your browser — no file to hand-edit.\n");
96
+ let captured;
97
+ try {
98
+ captured = await captureOnePasswordConfigViaBrowser({
99
+ providers: [...SUPPORTED_PROVIDERS],
100
+ existing,
101
+ existingAuthMode,
102
+ });
103
+ }
104
+ catch (err) {
105
+ process.stderr.write(`\n✗ ${err instanceof Error ? err.message : String(err)}\n\n`);
106
+ process.exitCode = 1;
107
+ return;
108
+ }
109
+ if (Object.keys(captured.mappings).length === 0) {
110
+ process.stderr.write("\n✗ No providers were mapped — nothing to enable.\n\n");
111
+ process.exitCode = 1;
112
+ return;
113
+ }
114
+ const written = writeOnePasswordConfig(captured);
115
+ if (!written.ok) {
116
+ process.stderr.write(`\n✗ ${written.error}: ${written.detail}\n\n`);
117
+ process.exitCode = 1;
118
+ return;
119
+ }
120
+ process.stdout.write(`✓ Saved ${configFilePath()}\n`);
121
+ }
122
+ process.stdout.write("\nValidating your 1Password configuration...\n");
123
+ const result = await validateOnePasswordSetup();
124
+ if (!result.ok) {
125
+ process.stderr.write(`\n✗ ${result.detail}\n`);
126
+ if (result.stage === "config") {
127
+ process.stderr.write(`\nExpected a config at ${configFilePath()} shaped like:\n` +
128
+ ' {\n "authMode": "desktop",\n "mappings": { "anthropic": "op://Vault/Item/field" }\n }\n' +
129
+ '\n(authMode is "desktop" or "service-account"; for service-account mode, set\n' +
130
+ "OP_SERVICE_ACCOUNT_TOKEN in the environment — never store it in this file.)\n");
131
+ }
132
+ if (result.stage === "op-version") {
133
+ process.stderr.write(`\nRequires the 1Password CLI (\`op\`), version >= ${MIN_OP_VERSION}.\n`);
134
+ }
135
+ if (result.providers)
136
+ printProviderResults(result.providers);
137
+ process.stderr.write("\nFix the above, then re-run `crosscheck keys setup --backend 1password`.\n\n");
138
+ process.exitCode = 1;
139
+ return;
140
+ }
141
+ enableOnePasswordBackend();
142
+ process.stdout.write(`\n✓ 1Password backend enabled (authMode: ${result.authMode}).\n\n`);
143
+ printProviderResults(result.providers);
144
+ process.stdout.write("\nMachine-local secrets (activation, HMAC signing key) still use the local\n" +
145
+ "encrypted file, unchanged — only providers marked ✓ above are read from\n" +
146
+ "1Password. Anything else (including a ✗ above) falls back to whatever's\n" +
147
+ "already stored locally, if anything.\n\n" +
148
+ "Run `crosscheck doctor` any time to recheck this.\n\n");
149
+ }
150
+ //# sourceMappingURL=setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.js","sourceRoot":"","sources":["../../src/onepassword/setup.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "crosscheck-cli",
3
- "version": "0.0.44",
3
+ "version": "0.0.46",
4
4
  "description": "Crosscheck — multi-LLM orchestration MCP for the terminal. Single-machine activation, BYO upstream LLM keys, content-free telemetry. Installs into Claude Code in one command.",
5
5
  "license": "SEE LICENSE.md",
6
6
  "type": "module",