@indigoai-us/hq-cli 5.5.2 → 5.5.3
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/dist/bin/hq-auth-refresh.js +26 -13
- package/dist/commands/_patterns.d.ts +3 -0
- package/dist/commands/_patterns.js +6 -0
- package/dist/commands/add.js +4 -1
- package/dist/commands/auth.js +4 -1
- package/dist/commands/cloud.js +4 -1
- package/dist/commands/groups.d.ts +3 -0
- package/dist/commands/groups.js +344 -0
- package/dist/commands/list.js +4 -1
- package/dist/commands/login.js +4 -1
- package/dist/commands/logout.js +4 -1
- package/dist/commands/onboard.js +4 -1
- package/dist/commands/pack-install.js +4 -1
- package/dist/commands/pkg-install.js +4 -1
- package/dist/commands/pkg-list.js +4 -1
- package/dist/commands/pkg-remove.js +4 -1
- package/dist/commands/pkg-update.js +4 -1
- package/dist/commands/secrets.d.ts +9 -0
- package/dist/commands/secrets.js +263 -25
- package/dist/commands/sync.js +4 -1
- package/dist/commands/team-sync.js +4 -1
- package/dist/commands/update.js +4 -1
- package/dist/commands/whoami.js +4 -1
- package/dist/index.js +21 -2
- package/dist/sentry-before-send.d.ts +3 -0
- package/dist/sentry-before-send.js +219 -0
- package/dist/sentry-dsn.generated.d.ts +2 -0
- package/dist/sentry-dsn.generated.js +5 -0
- package/dist/sentry.d.ts +4 -0
- package/dist/sentry.js +22 -0
- package/dist/strategies/link.js +4 -1
- package/dist/strategies/merge.js +4 -1
- package/dist/types.js +4 -1
- package/dist/utils/cognito-session.js +4 -1
- package/dist/utils/git.js +4 -1
- package/dist/utils/hq-root.js +4 -1
- package/dist/utils/integrity.js +4 -1
- package/dist/utils/manifest.js +4 -1
- package/dist/utils/registry-client.js +4 -1
- package/dist/utils/registry.js +4 -1
- package/dist/utils/secrets-cache.js +6 -2
- package/package.json +4 -3
- package/scripts/generate-dsn.mjs +26 -0
- package/src/bin/hq-auth-refresh.ts +23 -16
- package/src/commands/_patterns.ts +2 -0
- package/src/commands/groups.ts +357 -0
- package/src/commands/secrets.ts +306 -26
- package/src/index.ts +17 -1
- package/src/sentry-before-send.test.ts +359 -0
- package/src/sentry-before-send.ts +221 -0
- package/src/sentry.test.ts +24 -0
- package/src/sentry.ts +19 -0
- package/src/utils/secrets-cache.ts +2 -1
- package/dist/bin/hq-auth-refresh.d.ts.map +0 -1
- package/dist/bin/hq-auth-refresh.js.map +0 -1
- package/dist/commands/add.d.ts.map +0 -1
- package/dist/commands/add.js.map +0 -1
- package/dist/commands/auth.d.ts.map +0 -1
- package/dist/commands/auth.js.map +0 -1
- package/dist/commands/cloud.d.ts.map +0 -1
- package/dist/commands/cloud.js.map +0 -1
- package/dist/commands/list.d.ts.map +0 -1
- package/dist/commands/list.js.map +0 -1
- package/dist/commands/login.d.ts.map +0 -1
- package/dist/commands/login.js.map +0 -1
- package/dist/commands/logout.d.ts.map +0 -1
- package/dist/commands/logout.js.map +0 -1
- package/dist/commands/onboard.d.ts.map +0 -1
- package/dist/commands/onboard.js.map +0 -1
- package/dist/commands/pack-install.d.ts.map +0 -1
- package/dist/commands/pack-install.js.map +0 -1
- package/dist/commands/pkg-install.d.ts.map +0 -1
- package/dist/commands/pkg-install.js.map +0 -1
- package/dist/commands/pkg-list.d.ts.map +0 -1
- package/dist/commands/pkg-list.js.map +0 -1
- package/dist/commands/pkg-remove.d.ts.map +0 -1
- package/dist/commands/pkg-remove.js.map +0 -1
- package/dist/commands/pkg-update.d.ts.map +0 -1
- package/dist/commands/pkg-update.js.map +0 -1
- package/dist/commands/secrets.d.ts.map +0 -1
- package/dist/commands/secrets.js.map +0 -1
- package/dist/commands/sync.d.ts.map +0 -1
- package/dist/commands/sync.js.map +0 -1
- package/dist/commands/team-sync.d.ts.map +0 -1
- package/dist/commands/team-sync.js.map +0 -1
- package/dist/commands/update.d.ts.map +0 -1
- package/dist/commands/update.js.map +0 -1
- package/dist/commands/whoami.d.ts.map +0 -1
- package/dist/commands/whoami.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/strategies/link.d.ts.map +0 -1
- package/dist/strategies/link.js.map +0 -1
- package/dist/strategies/merge.d.ts.map +0 -1
- package/dist/strategies/merge.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/utils/auth.d.ts +0 -27
- package/dist/utils/auth.d.ts.map +0 -1
- package/dist/utils/auth.js +0 -155
- package/dist/utils/auth.js.map +0 -1
- package/dist/utils/cognito-session.d.ts.map +0 -1
- package/dist/utils/cognito-session.js.map +0 -1
- package/dist/utils/git.d.ts.map +0 -1
- package/dist/utils/git.js.map +0 -1
- package/dist/utils/hq-root.d.ts.map +0 -1
- package/dist/utils/hq-root.js.map +0 -1
- package/dist/utils/integrity.d.ts.map +0 -1
- package/dist/utils/integrity.js.map +0 -1
- package/dist/utils/manifest.d.ts.map +0 -1
- package/dist/utils/manifest.js.map +0 -1
- package/dist/utils/registry-client.d.ts.map +0 -1
- package/dist/utils/registry-client.js.map +0 -1
- package/dist/utils/registry.d.ts.map +0 -1
- package/dist/utils/registry.js.map +0 -1
- package/dist/utils/secrets-cache.d.ts.map +0 -1
- package/dist/utils/secrets-cache.js.map +0 -1
- package/dist/utils/token-store.d.ts +0 -28
- package/dist/utils/token-store.d.ts.map +0 -1
- package/dist/utils/token-store.js +0 -68
- package/dist/utils/token-store.js.map +0 -1
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
// src/sentry-before-send.test.ts — canonical fleet Vitest suite.
|
|
2
|
+
// This file is byte-identical across every consumer repo (no per-repo
|
|
3
|
+
// line-1 variation, unlike sentry-before-send.ts). The body-identity
|
|
4
|
+
// gate each consumer runs is `diff hq-console/src/sentry-before-send.test.ts
|
|
5
|
+
// <consumer>/src/sentry-before-send.test.ts` returns empty (no tail -n +2).
|
|
6
|
+
import { describe, expect, it } from "vitest";
|
|
7
|
+
import type { ErrorEvent, EventHint } from "@sentry/nextjs";
|
|
8
|
+
import { beforeSend } from "./sentry-before-send";
|
|
9
|
+
|
|
10
|
+
const REDACTED = "[Filtered]";
|
|
11
|
+
|
|
12
|
+
function run(event: ErrorEvent): ErrorEvent {
|
|
13
|
+
const result = beforeSend(event, {} as EventHint);
|
|
14
|
+
if (!result) throw new Error("beforeSend dropped event unexpectedly");
|
|
15
|
+
return result;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
describe("scrubHeaders — SENSITIVE_HEADER_NAMES case-insensitive redaction", () => {
|
|
19
|
+
const sensitiveHeaders = [
|
|
20
|
+
"authorization",
|
|
21
|
+
"proxy-authorization",
|
|
22
|
+
"cookie",
|
|
23
|
+
"set-cookie",
|
|
24
|
+
"x-api-key",
|
|
25
|
+
"x-auth-token",
|
|
26
|
+
"x-access-token",
|
|
27
|
+
"x-session-token",
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
it.each(sensitiveHeaders)("redacts %s header (and case variants)", (name) => {
|
|
31
|
+
const upper = name.toUpperCase();
|
|
32
|
+
const out = run({
|
|
33
|
+
request: {
|
|
34
|
+
headers: { [upper]: "leaky-value", "x-trace-id": "keep-me" },
|
|
35
|
+
},
|
|
36
|
+
} as unknown as ErrorEvent);
|
|
37
|
+
expect((out.request!.headers as Record<string, unknown>)[upper]).toBe(REDACTED);
|
|
38
|
+
expect((out.request!.headers as Record<string, unknown>)["x-trace-id"]).toBe("keep-me");
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("redacts a raw Cookie header that lands on request.headers.cookie", () => {
|
|
42
|
+
const out = run({
|
|
43
|
+
request: {
|
|
44
|
+
headers: {
|
|
45
|
+
Cookie: "authjs.session-token=abc; other=keep",
|
|
46
|
+
"x-trace-id": "keep-me",
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
} as unknown as ErrorEvent);
|
|
50
|
+
expect((out.request!.headers as Record<string, unknown>).Cookie).toBe(REDACTED);
|
|
51
|
+
expect((out.request!.headers as Record<string, unknown>)["x-trace-id"]).toBe("keep-me");
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe("scrubCookies — SENSITIVE_COOKIE_NAMES (Auth.js v5 + v4 legacy + Clerk) + raw-string fallback", () => {
|
|
56
|
+
// v5 (Auth.js): session / callback-url use __Secure-; csrf-token uses
|
|
57
|
+
// __Host- (NOT __Secure-). OAuth-state families (pkce / state / nonce /
|
|
58
|
+
// challenge) each pair bare + __Secure-.
|
|
59
|
+
const v5Names = [
|
|
60
|
+
"authjs.session-token",
|
|
61
|
+
"__secure-authjs.session-token",
|
|
62
|
+
"authjs.csrf-token",
|
|
63
|
+
"__host-authjs.csrf-token",
|
|
64
|
+
"authjs.callback-url",
|
|
65
|
+
"__secure-authjs.callback-url",
|
|
66
|
+
"authjs.pkce.code_verifier",
|
|
67
|
+
"__secure-authjs.pkce.code_verifier",
|
|
68
|
+
"authjs.state",
|
|
69
|
+
"__secure-authjs.state",
|
|
70
|
+
"authjs.nonce",
|
|
71
|
+
"__secure-authjs.nonce",
|
|
72
|
+
"authjs.challenge",
|
|
73
|
+
"__secure-authjs.challenge",
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
// v4 (NextAuth legacy): same prefix discipline — csrf uses __Host-, others
|
|
77
|
+
// use __Secure-. Kept in the set so accidental downgrades / mixed-version
|
|
78
|
+
// monorepos don't silently leak.
|
|
79
|
+
const v4Names = [
|
|
80
|
+
"next-auth.session-token",
|
|
81
|
+
"__secure-next-auth.session-token",
|
|
82
|
+
"next-auth.csrf-token",
|
|
83
|
+
"__host-next-auth.csrf-token",
|
|
84
|
+
"next-auth.callback-url",
|
|
85
|
+
"__secure-next-auth.callback-url",
|
|
86
|
+
"next-auth.pkce.code_verifier",
|
|
87
|
+
"__secure-next-auth.pkce.code_verifier",
|
|
88
|
+
"next-auth.state",
|
|
89
|
+
"__secure-next-auth.state",
|
|
90
|
+
];
|
|
91
|
+
|
|
92
|
+
const clerkNames = ["__clerk_db_jwt", "__session"];
|
|
93
|
+
|
|
94
|
+
it.each([...v5Names, ...v4Names, ...clerkNames])(
|
|
95
|
+
"redacts cookie %s (case-insensitive)",
|
|
96
|
+
(name) => {
|
|
97
|
+
const out = run({
|
|
98
|
+
request: {
|
|
99
|
+
cookies: { [name.toUpperCase()]: "secret-session-value", keep_me: "ok" },
|
|
100
|
+
},
|
|
101
|
+
} as unknown as ErrorEvent);
|
|
102
|
+
const cookies = out.request!.cookies as Record<string, unknown>;
|
|
103
|
+
expect(cookies[name.toUpperCase()]).toBe(REDACTED);
|
|
104
|
+
expect(cookies.keep_me).toBe("ok");
|
|
105
|
+
},
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
it("does NOT include a __Secure-authjs.csrf-token assertion — Auth.js v5 never emits that name (uses __Host- instead)", () => {
|
|
109
|
+
// This test is a read-through spec comment: the set MUST NOT have an
|
|
110
|
+
// entry for __Secure-authjs.csrf-token. If a maintainer ever adds one
|
|
111
|
+
// mechanically-by-pattern, this test documents why it is wrong.
|
|
112
|
+
const out = run({
|
|
113
|
+
request: {
|
|
114
|
+
cookies: { "__Secure-authjs.csrf-token": "does-not-exist-in-prod" },
|
|
115
|
+
},
|
|
116
|
+
} as unknown as ErrorEvent);
|
|
117
|
+
// The value is NOT redacted because the set does not contain that name.
|
|
118
|
+
// This is deliberate — see SENSITIVE_COOKIE_NAMES comment in the module.
|
|
119
|
+
const cookies = out.request!.cookies as Record<string, unknown>;
|
|
120
|
+
expect(cookies["__Secure-authjs.csrf-token"]).toBe("does-not-exist-in-prod");
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("wholesale-redacts a raw-string Cookie header", () => {
|
|
124
|
+
const out = run({
|
|
125
|
+
request: { cookies: "anything=here; other=keep" },
|
|
126
|
+
} as unknown as ErrorEvent);
|
|
127
|
+
expect(out.request!.cookies).toBe(REDACTED);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it("leaves undefined cookies alone", () => {
|
|
131
|
+
const out = run({ request: { headers: {} } } as unknown as ErrorEvent);
|
|
132
|
+
expect(out.request!.cookies).toBeUndefined();
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
describe("scrubFields — recursive redaction with case-insensitivity, arrays, nested, cycles", () => {
|
|
137
|
+
it("redacts top-level sensitive field names case-insensitively", () => {
|
|
138
|
+
const out = run({
|
|
139
|
+
extra: {
|
|
140
|
+
token: "abc",
|
|
141
|
+
Password: "pw",
|
|
142
|
+
APIKEY: "k",
|
|
143
|
+
api_key: "k2",
|
|
144
|
+
IdToken: "id",
|
|
145
|
+
ACCESSTOKEN: "at",
|
|
146
|
+
RefreshToken: "rt",
|
|
147
|
+
keep: "yes",
|
|
148
|
+
},
|
|
149
|
+
} as unknown as ErrorEvent);
|
|
150
|
+
const extra = out.extra as Record<string, unknown>;
|
|
151
|
+
expect(extra.token).toBe(REDACTED);
|
|
152
|
+
expect(extra.Password).toBe(REDACTED);
|
|
153
|
+
expect(extra.APIKEY).toBe(REDACTED);
|
|
154
|
+
expect(extra.api_key).toBe(REDACTED);
|
|
155
|
+
expect(extra.IdToken).toBe(REDACTED);
|
|
156
|
+
expect(extra.ACCESSTOKEN).toBe(REDACTED);
|
|
157
|
+
expect(extra.RefreshToken).toBe(REDACTED);
|
|
158
|
+
expect(extra.keep).toBe("yes");
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it("recurses into nested objects and arrays", () => {
|
|
162
|
+
const out = run({
|
|
163
|
+
extra: {
|
|
164
|
+
nested: { deeper: { token: "t", safe: 1 } },
|
|
165
|
+
items: [{ password: "pw" }, { other: "ok" }],
|
|
166
|
+
},
|
|
167
|
+
} as unknown as ErrorEvent);
|
|
168
|
+
const extra = out.extra as Record<string, any>;
|
|
169
|
+
expect(extra.nested.deeper.token).toBe(REDACTED);
|
|
170
|
+
expect(extra.nested.deeper.safe).toBe(1);
|
|
171
|
+
expect(extra.items[0].password).toBe(REDACTED);
|
|
172
|
+
expect(extra.items[1].other).toBe("ok");
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it("handles circular references without stack overflow and scrubs fields reachable only through the cycle", () => {
|
|
176
|
+
const cyclic: Record<string, unknown> = { token: "leak" };
|
|
177
|
+
cyclic.self = cyclic;
|
|
178
|
+
const out = run({ extra: { outer: cyclic } } as unknown as ErrorEvent);
|
|
179
|
+
const outer = (out.extra as any).outer;
|
|
180
|
+
// Top-level field is scrubbed.
|
|
181
|
+
expect(outer.token).toBe(REDACTED);
|
|
182
|
+
// The Map<original, scrubbed> cycle fix means re-visits return the
|
|
183
|
+
// memoized scrubbed copy, not the un-scrubbed original — so
|
|
184
|
+
// `outer.self.token` is ALSO [Filtered], not the leaked value.
|
|
185
|
+
expect(outer.self.token).toBe(REDACTED);
|
|
186
|
+
expect(outer.self).toBe(outer);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it("scalar-string defense-in-depth: redacts embedded credentials in extra.note even when the key is NOT in SENSITIVE_FIELD_NAMES", () => {
|
|
190
|
+
const out = run({
|
|
191
|
+
extra: { note: "Something broke, password=hunter2 failed" },
|
|
192
|
+
} as unknown as ErrorEvent);
|
|
193
|
+
const extra = out.extra as Record<string, unknown>;
|
|
194
|
+
expect(extra.note).toContain(`password=${REDACTED}`);
|
|
195
|
+
expect(extra.note).not.toContain("hunter2");
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it("scalar-string defense-in-depth: redacts a Bearer in user.bio (key NOT in SENSITIVE_FIELD_NAMES)", () => {
|
|
199
|
+
const out = run({
|
|
200
|
+
user: { id: "u1", email: "a@b.c", bio: "authorization: Bearer leak" },
|
|
201
|
+
} as unknown as ErrorEvent);
|
|
202
|
+
const user = out.user as Record<string, unknown>;
|
|
203
|
+
expect(user.id).toBe("u1");
|
|
204
|
+
expect(user.email).toBe("a@b.c");
|
|
205
|
+
expect(user.bio).toContain(`Bearer ${REDACTED}`);
|
|
206
|
+
expect(user.bio).not.toContain("leak");
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
describe("scrubString — regex passes for kv, scheme+credential, JWT triplets", () => {
|
|
211
|
+
it("redacts key=value and key: value pairs", () => {
|
|
212
|
+
const out = run({
|
|
213
|
+
message: "password=hunter2 and api-key: abc123 should be redacted",
|
|
214
|
+
} as unknown as ErrorEvent);
|
|
215
|
+
expect(out.message).toContain(`password=${REDACTED}`);
|
|
216
|
+
expect(out.message).toContain(`api-key:${REDACTED}`);
|
|
217
|
+
expect(out.message).not.toContain("hunter2");
|
|
218
|
+
expect(out.message).not.toContain("abc123");
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it("redacts Bearer / Basic credentials", () => {
|
|
222
|
+
const out = run({
|
|
223
|
+
message: "Got header Bearer abcDEF._~+/=- and Basic Zm9vOmJhcg==",
|
|
224
|
+
} as unknown as ErrorEvent);
|
|
225
|
+
expect(out.message).toContain(`Bearer ${REDACTED}`);
|
|
226
|
+
expect(out.message).toContain(`Basic ${REDACTED}`);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it("redacts non-JWT-shaped accessToken/refreshToken/id_token values in free text", () => {
|
|
230
|
+
const out = run({
|
|
231
|
+
message:
|
|
232
|
+
"refreshToken=opaqueBase64NotAJwt-xyz and accessToken: anotherOpaqueValue and id_token=short",
|
|
233
|
+
} as unknown as ErrorEvent);
|
|
234
|
+
expect(out.message).toContain(`refreshToken=${REDACTED}`);
|
|
235
|
+
expect(out.message).toContain(`accessToken:${REDACTED}`);
|
|
236
|
+
expect(out.message).toContain(`id_token=${REDACTED}`);
|
|
237
|
+
expect(out.message).not.toContain("opaqueBase64NotAJwt-xyz");
|
|
238
|
+
expect(out.message).not.toContain("anotherOpaqueValue");
|
|
239
|
+
expect(out.message).not.toContain("=short");
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it("redacts bare JWT triplets", () => {
|
|
243
|
+
const jwt = "eyJhbGciOi12345.eyJzdWIiOi67890.sig_abcdef1234";
|
|
244
|
+
const out = run({ message: `token was ${jwt} yeah` } as unknown as ErrorEvent);
|
|
245
|
+
expect(out.message).not.toContain(jwt);
|
|
246
|
+
expect(out.message).toContain(REDACTED);
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
describe("scrubQueryString — parse + fallback", () => {
|
|
251
|
+
it("parses well-formed query strings with a leading ? and redacts sensitive keys", () => {
|
|
252
|
+
const out = run({
|
|
253
|
+
request: {
|
|
254
|
+
query_string: "?foo=bar&token=leak&password=pw&keep=me",
|
|
255
|
+
},
|
|
256
|
+
} as unknown as ErrorEvent);
|
|
257
|
+
const qs = out.request!.query_string as string;
|
|
258
|
+
expect(qs.startsWith("?")).toBe(true);
|
|
259
|
+
expect(qs).toContain("foo=bar");
|
|
260
|
+
expect(qs).toContain(`token=${REDACTED}`);
|
|
261
|
+
expect(qs).toContain(`password=${REDACTED}`);
|
|
262
|
+
expect(qs).toContain("keep=me");
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it("parses no-leading-? strings as well", () => {
|
|
266
|
+
const out = run({
|
|
267
|
+
request: { query_string: "token=leak&keep=1" },
|
|
268
|
+
} as unknown as ErrorEvent);
|
|
269
|
+
const qs = out.request!.query_string as string;
|
|
270
|
+
expect(qs.startsWith("?")).toBe(false);
|
|
271
|
+
expect(qs).toContain(`token=${REDACTED}`);
|
|
272
|
+
expect(qs).toContain("keep=1");
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it("leaves non-string query_string alone", () => {
|
|
276
|
+
const out = run({
|
|
277
|
+
request: { query_string: undefined },
|
|
278
|
+
} as unknown as ErrorEvent);
|
|
279
|
+
expect(out.request!.query_string).toBeUndefined();
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
describe("event-level walk — exception.values, message, breadcrumbs, user, extra, contexts, request.data", () => {
|
|
284
|
+
it("scrubs exception.values[].value strings", () => {
|
|
285
|
+
const out = run({
|
|
286
|
+
exception: {
|
|
287
|
+
values: [
|
|
288
|
+
{ type: "Error", value: "password=sekret failed" },
|
|
289
|
+
{ type: "Error", value: "clean" },
|
|
290
|
+
],
|
|
291
|
+
},
|
|
292
|
+
} as unknown as ErrorEvent);
|
|
293
|
+
expect(out.exception!.values![0].value).toContain(`password=${REDACTED}`);
|
|
294
|
+
expect(out.exception!.values![0].value).not.toContain("sekret");
|
|
295
|
+
expect(out.exception!.values![1].value).toBe("clean");
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
it("scrubs event.message", () => {
|
|
299
|
+
const out = run({ message: "Bearer leaked.jwt.token" } as unknown as ErrorEvent);
|
|
300
|
+
expect(out.message).toContain(`Bearer ${REDACTED}`);
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it("scrubs breadcrumbs.message (string) and breadcrumbs.data (structured)", () => {
|
|
304
|
+
const out = run({
|
|
305
|
+
breadcrumbs: [
|
|
306
|
+
{
|
|
307
|
+
category: "fetch",
|
|
308
|
+
message: "GET /api?token=abc",
|
|
309
|
+
data: { url: "/api", password: "leak" },
|
|
310
|
+
},
|
|
311
|
+
],
|
|
312
|
+
} as unknown as ErrorEvent);
|
|
313
|
+
const bc = out.breadcrumbs![0];
|
|
314
|
+
expect(bc.message).toContain(`token=${REDACTED}`);
|
|
315
|
+
expect((bc.data as Record<string, unknown>).password).toBe(REDACTED);
|
|
316
|
+
expect((bc.data as Record<string, unknown>).url).toBe("/api");
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
it("scrubs event.user fields", () => {
|
|
320
|
+
const out = run({
|
|
321
|
+
user: { id: "u1", email: "a@b.c", token: "bad", IdToken: "cognito" },
|
|
322
|
+
} as unknown as ErrorEvent);
|
|
323
|
+
const user = out.user as Record<string, unknown>;
|
|
324
|
+
expect(user.id).toBe("u1");
|
|
325
|
+
expect(user.email).toBe("a@b.c");
|
|
326
|
+
expect(user.token).toBe(REDACTED);
|
|
327
|
+
expect(user.IdToken).toBe(REDACTED);
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it("scrubs event.extra", () => {
|
|
331
|
+
const out = run({ extra: { password: "x", safe: "y" } } as unknown as ErrorEvent);
|
|
332
|
+
expect((out.extra as Record<string, unknown>).password).toBe(REDACTED);
|
|
333
|
+
expect((out.extra as Record<string, unknown>).safe).toBe("y");
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
it("scrubs event.contexts", () => {
|
|
337
|
+
const out = run({
|
|
338
|
+
contexts: {
|
|
339
|
+
auth: { token: "x", scheme: "bearer" },
|
|
340
|
+
runtime: { name: "node", version: "22" },
|
|
341
|
+
},
|
|
342
|
+
} as unknown as ErrorEvent);
|
|
343
|
+
const auth = (out.contexts as any).auth;
|
|
344
|
+
const runtime = (out.contexts as any).runtime;
|
|
345
|
+
expect(auth.token).toBe(REDACTED);
|
|
346
|
+
expect(auth.scheme).toBe("bearer");
|
|
347
|
+
expect(runtime.name).toBe("node");
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
it("scrubs structured request body via request.data", () => {
|
|
351
|
+
const out = run({
|
|
352
|
+
request: { data: { username: "u", password: "pw", nested: { token: "t" } } },
|
|
353
|
+
} as unknown as ErrorEvent);
|
|
354
|
+
const data = out.request!.data as Record<string, any>;
|
|
355
|
+
expect(data.username).toBe("u");
|
|
356
|
+
expect(data.password).toBe(REDACTED);
|
|
357
|
+
expect(data.nested.token).toBe(REDACTED);
|
|
358
|
+
});
|
|
359
|
+
});
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
// src/sentry-before-send.ts — canonical fleet scrubber
|
|
2
|
+
// Line 1 is per-repo — see "Per-repo `import type` line" table in Step 3.
|
|
3
|
+
// hq-console uses @sentry/nextjs. Consumer steps override to their own SDK.
|
|
4
|
+
import type { ErrorEvent, EventHint } from "@sentry/node";
|
|
5
|
+
|
|
6
|
+
const SENSITIVE_FIELD_NAMES = new Set([
|
|
7
|
+
"password", "secret", "apikey", "api_key", "token",
|
|
8
|
+
// Cognito token-response fields captured as error context.
|
|
9
|
+
"idtoken", "accesstoken", "refreshtoken",
|
|
10
|
+
]);
|
|
11
|
+
|
|
12
|
+
// Stored lowercased; membership tested against `name.toLowerCase()`.
|
|
13
|
+
// MUST cover BOTH Auth.js v5 (current in hq-console, pinned to
|
|
14
|
+
// next-auth@5.0.0-beta.25 which resolves @auth/core@0.37.2) AND legacy
|
|
15
|
+
// NextAuth v4 name spaces — covering both is cheap and survives accidental
|
|
16
|
+
// downgrades or mixed-version repos. Grounded against @auth/core@0.37.2's
|
|
17
|
+
// src/lib/utils/cookie.ts (re-ground via `grep -nE
|
|
18
|
+
// '(sessionToken|callbackUrl|csrfToken|pkceCodeVerifier|state|nonce|webauthnChallenge): \{'
|
|
19
|
+
// node_modules/.pnpm/@auth+core@0.37.2/node_modules/@auth/core/src/lib/utils/cookie.ts`
|
|
20
|
+
// — seven matches, one per cookie family): two distinct prefixing patterns
|
|
21
|
+
// coexist there, and the entries below mirror BOTH exactly:
|
|
22
|
+
// • session-token / callback-url / pkce.code_verifier / state / nonce /
|
|
23
|
+
// challenge each pair the bare form (HTTP dev, cookiePrefix="") with
|
|
24
|
+
// the __Secure- form (HTTPS prod, cookiePrefix="__Secure-").
|
|
25
|
+
// • csrf-token is special: the source explicitly uses __Host- (NOT
|
|
26
|
+
// __Secure-) under HTTPS for stricter cookie scoping (the csrfToken
|
|
27
|
+
// entry in @auth/core's cookie.ts is the only one whose name template
|
|
28
|
+
// uses `__Host-` instead of `${cookiePrefix}`; the comment above it
|
|
29
|
+
// reads "Default to __Host- for CSRF token for additional protection
|
|
30
|
+
// if using useSecureCookies"). The set therefore pairs the bare form
|
|
31
|
+
// with __Host-, and does NOT include __Secure-authjs.csrf-token
|
|
32
|
+
// because @auth/core never emits that name. The legacy v4 entry mirrors
|
|
33
|
+
// the same pattern (bare next-auth.csrf-token + __Host-next-auth.csrf-token,
|
|
34
|
+
// no __Secure- variant).
|
|
35
|
+
// Missing any of the four OAuth-state families (pkce.code_verifier, state,
|
|
36
|
+
// nonce, challenge) ships a cookie-leak in the same class as the v4-only
|
|
37
|
+
// defect Round-4 called out. This set is the single authoritative spec;
|
|
38
|
+
// all of Steps 3/8/9a/12/13 reproduce it identically.
|
|
39
|
+
//
|
|
40
|
+
// MAINTAINER NOTE: Do NOT extend this set by mechanically pairing every
|
|
41
|
+
// cookie with bare + __Secure- + __Host-. The pairing follows the source:
|
|
42
|
+
// session-style cookies use __Secure-; csrf-style cookies use __Host-.
|
|
43
|
+
// New entries added in the future must be grounded against the actual
|
|
44
|
+
// emitter (Auth.js source for v5/v4, vendor docs for Clerk, etc.) rather
|
|
45
|
+
// than copied from the existing pattern.
|
|
46
|
+
const SENSITIVE_COOKIE_NAMES = new Set([
|
|
47
|
+
// ==== Auth.js v5 (next-auth@5 / @auth/core) ====
|
|
48
|
+
// Session JWT — bare + __Secure-
|
|
49
|
+
"authjs.session-token",
|
|
50
|
+
"__secure-authjs.session-token",
|
|
51
|
+
// CSRF double-submit token — bare + __Host- ONLY (@auth/core's cookie.ts
|
|
52
|
+
// csrfToken entry uses __Host-, never __Secure-)
|
|
53
|
+
"authjs.csrf-token",
|
|
54
|
+
"__host-authjs.csrf-token",
|
|
55
|
+
// Post-auth redirect URL — bare + __Secure-
|
|
56
|
+
"authjs.callback-url",
|
|
57
|
+
"__secure-authjs.callback-url",
|
|
58
|
+
// OAuth PKCE verifier — leaking this enables interception of an in-flight
|
|
59
|
+
// OAuth code exchange.
|
|
60
|
+
"authjs.pkce.code_verifier",
|
|
61
|
+
"__secure-authjs.pkce.code_verifier",
|
|
62
|
+
// OAuth CSRF/replay protection
|
|
63
|
+
"authjs.state",
|
|
64
|
+
"__secure-authjs.state",
|
|
65
|
+
// OpenID Connect nonce — replay protection
|
|
66
|
+
"authjs.nonce",
|
|
67
|
+
"__secure-authjs.nonce",
|
|
68
|
+
// OAuth WebAuthn challenge
|
|
69
|
+
"authjs.challenge",
|
|
70
|
+
"__secure-authjs.challenge",
|
|
71
|
+
// ==== NextAuth v4 legacy names (kept so accidental downgrades /
|
|
72
|
+
// mixed-version monorepos don't silently leak). Same prefix
|
|
73
|
+
// discipline as v5: csrf-token uses __Host-, others use __Secure-. ====
|
|
74
|
+
"next-auth.session-token",
|
|
75
|
+
"__secure-next-auth.session-token",
|
|
76
|
+
"next-auth.csrf-token",
|
|
77
|
+
"__host-next-auth.csrf-token",
|
|
78
|
+
"next-auth.callback-url",
|
|
79
|
+
"__secure-next-auth.callback-url",
|
|
80
|
+
"next-auth.pkce.code_verifier",
|
|
81
|
+
"__secure-next-auth.pkce.code_verifier",
|
|
82
|
+
"next-auth.state",
|
|
83
|
+
"__secure-next-auth.state",
|
|
84
|
+
// ==== Clerk (documented for future; no-op today) ====
|
|
85
|
+
"__clerk_db_jwt", "__session",
|
|
86
|
+
]);
|
|
87
|
+
|
|
88
|
+
const REDACTED = "[Filtered]";
|
|
89
|
+
|
|
90
|
+
const SENSITIVE_HEADER_NAMES = new Set([
|
|
91
|
+
"authorization", "proxy-authorization",
|
|
92
|
+
"cookie", "set-cookie",
|
|
93
|
+
"x-api-key", "x-auth-token", "x-access-token", "x-session-token",
|
|
94
|
+
]);
|
|
95
|
+
|
|
96
|
+
function scrubHeaders(h?: Record<string, unknown>): Record<string, unknown> | undefined {
|
|
97
|
+
if (!h) return h;
|
|
98
|
+
const out: Record<string, unknown> = {};
|
|
99
|
+
for (const [k, v] of Object.entries(h)) {
|
|
100
|
+
out[k] = SENSITIVE_HEADER_NAMES.has(k.toLowerCase()) ? REDACTED : v;
|
|
101
|
+
}
|
|
102
|
+
return out;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
type CookieField = Record<string, unknown> | string | undefined;
|
|
106
|
+
|
|
107
|
+
function scrubCookies(c: CookieField): CookieField {
|
|
108
|
+
if (!c) return c;
|
|
109
|
+
if (typeof c === "string") return REDACTED; // raw Cookie header — wholesale redact
|
|
110
|
+
const out: Record<string, unknown> = {};
|
|
111
|
+
for (const [k, v] of Object.entries(c)) {
|
|
112
|
+
out[k] = SENSITIVE_COOKIE_NAMES.has(k.toLowerCase()) ? REDACTED : v;
|
|
113
|
+
}
|
|
114
|
+
return out;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const SENSITIVE_STRING_PATTERNS: RegExp[] = [
|
|
118
|
+
// kv pairs; `(?:id|access|refresh)[_-]?token` catches Cognito-style free-text
|
|
119
|
+
// `accessToken=...`, `refreshToken: ...`, `id_token=...` which bare `\btoken` misses.
|
|
120
|
+
/\b(password|secret|api[_-]?key|(?:id|access|refresh)[_-]?token|token)\s*[:=]\s*[^\s&"'<>]+/gi,
|
|
121
|
+
/\b(Bearer|Basic)\s+[A-Za-z0-9._~+/=-]+/gi,
|
|
122
|
+
/\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g,
|
|
123
|
+
];
|
|
124
|
+
|
|
125
|
+
function scrubString(s: string): string {
|
|
126
|
+
let out = s;
|
|
127
|
+
for (const re of SENSITIVE_STRING_PATTERNS) {
|
|
128
|
+
out = out.replace(re, (m) => {
|
|
129
|
+
const sep = m.search(/[:=\s]/);
|
|
130
|
+
return sep > 0 ? `${m.slice(0, sep + 1)}${REDACTED}` : REDACTED;
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
return out;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function scrubQueryString(qs: string): string {
|
|
137
|
+
try {
|
|
138
|
+
const params = new URLSearchParams(qs.startsWith("?") ? qs.slice(1) : qs);
|
|
139
|
+
const out: string[] = [];
|
|
140
|
+
for (const [k, v] of params) {
|
|
141
|
+
out.push(`${k}=${SENSITIVE_FIELD_NAMES.has(k.toLowerCase()) ? REDACTED : v}`);
|
|
142
|
+
}
|
|
143
|
+
const joined = out.join("&");
|
|
144
|
+
return qs.startsWith("?") ? `?${joined}` : joined;
|
|
145
|
+
} catch {
|
|
146
|
+
return scrubString(qs);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function scrubFields<T>(value: T, memo: Map<object, unknown> = new Map()): T {
|
|
151
|
+
if (value === null || typeof value !== "object") return value;
|
|
152
|
+
const obj = value as unknown as object;
|
|
153
|
+
// On cycle revisit, return the SAME scrubbed copy built on first visit —
|
|
154
|
+
// NOT the original. Returning the original would leak fields reachable
|
|
155
|
+
// only through the cycle (e.g., a.self.token).
|
|
156
|
+
if (memo.has(obj)) return memo.get(obj) as T;
|
|
157
|
+
|
|
158
|
+
if (Array.isArray(value)) {
|
|
159
|
+
const arr: unknown[] = [];
|
|
160
|
+
memo.set(obj, arr);
|
|
161
|
+
for (const v of value) arr.push(scrubFields(v, memo));
|
|
162
|
+
return arr as unknown as T;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const out: Record<string, unknown> = {};
|
|
166
|
+
memo.set(obj, out);
|
|
167
|
+
for (const [k, v] of Object.entries(value as Record<string, unknown>)) {
|
|
168
|
+
if (SENSITIVE_FIELD_NAMES.has(k.toLowerCase())) {
|
|
169
|
+
out[k] = REDACTED;
|
|
170
|
+
} else if (v !== null && typeof v === "object") {
|
|
171
|
+
out[k] = scrubFields(v, memo);
|
|
172
|
+
} else if (typeof v === "string") {
|
|
173
|
+
// SCALAR-STRING DEFENSE-IN-DEPTH. A stringified log line stashed via
|
|
174
|
+
// setExtra / setContext / setUser can embed `password=...` or a Bearer
|
|
175
|
+
// token even when the outer key (`note`, `bio`, `log_line`, …) isn't
|
|
176
|
+
// in SENSITIVE_FIELD_NAMES. Route scalar strings through scrubString so
|
|
177
|
+
// these don't bypass the scrubber on their way into Sentry.
|
|
178
|
+
out[k] = scrubString(v);
|
|
179
|
+
} else {
|
|
180
|
+
out[k] = v;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return out as unknown as T;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function beforeSend(event: ErrorEvent, _hint: EventHint): ErrorEvent | null {
|
|
187
|
+
if (event.request) {
|
|
188
|
+
const req = event.request as { headers?: unknown; cookies?: unknown; data?: unknown; query_string?: unknown };
|
|
189
|
+
event.request = {
|
|
190
|
+
...event.request,
|
|
191
|
+
headers: scrubHeaders(req.headers as Record<string, unknown> | undefined) as typeof event.request.headers,
|
|
192
|
+
cookies: scrubCookies(req.cookies as CookieField) as typeof event.request.cookies,
|
|
193
|
+
data: scrubFields(req.data),
|
|
194
|
+
query_string:
|
|
195
|
+
typeof req.query_string === "string"
|
|
196
|
+
? scrubQueryString(req.query_string)
|
|
197
|
+
: (req.query_string as typeof event.request.query_string),
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
if (typeof event.message === "string") event.message = scrubString(event.message);
|
|
201
|
+
if (event.exception?.values) {
|
|
202
|
+
event.exception = {
|
|
203
|
+
...event.exception,
|
|
204
|
+
values: event.exception.values.map((v) => ({
|
|
205
|
+
...v,
|
|
206
|
+
value: typeof v.value === "string" ? scrubString(v.value) : v.value,
|
|
207
|
+
})),
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
if (event.user) event.user = scrubFields(event.user as Record<string, unknown>) as typeof event.user;
|
|
211
|
+
if (event.breadcrumbs) {
|
|
212
|
+
event.breadcrumbs = event.breadcrumbs.map((b) => ({
|
|
213
|
+
...b,
|
|
214
|
+
message: typeof b.message === "string" ? scrubString(b.message) : b.message,
|
|
215
|
+
data: b.data ? scrubFields(b.data) : b.data,
|
|
216
|
+
}));
|
|
217
|
+
}
|
|
218
|
+
if (event.extra) event.extra = scrubFields(event.extra);
|
|
219
|
+
if (event.contexts) event.contexts = scrubFields(event.contexts);
|
|
220
|
+
return event;
|
|
221
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import * as Sentry from "@sentry/node";
|
|
3
|
+
import { initSentry } from "./sentry.js";
|
|
4
|
+
|
|
5
|
+
vi.mock("./sentry-dsn.generated.js", () => ({ BUNDLED_DSN: "" }));
|
|
6
|
+
|
|
7
|
+
describe("initSentry — empty BUNDLED_DSN no-op", () => {
|
|
8
|
+
afterEach(() => {
|
|
9
|
+
vi.unstubAllEnvs();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it("fakeTransport.send is never called after captureException + flush(2000) when no DSN configured", async () => {
|
|
13
|
+
vi.stubEnv("SENTRY_DSN", "");
|
|
14
|
+
|
|
15
|
+
const fakeTransport = { send: vi.fn().mockResolvedValue(undefined) };
|
|
16
|
+
|
|
17
|
+
initSentry();
|
|
18
|
+
|
|
19
|
+
Sentry.captureException(new Error("no-dsn-should-not-send"));
|
|
20
|
+
await Sentry.flush(2000);
|
|
21
|
+
|
|
22
|
+
expect(fakeTransport.send).not.toHaveBeenCalled();
|
|
23
|
+
});
|
|
24
|
+
});
|
package/src/sentry.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as Sentry from "@sentry/node";
|
|
2
|
+
import { BUNDLED_DSN } from "./sentry-dsn.generated.js";
|
|
3
|
+
import { beforeSend } from "./sentry-before-send.js";
|
|
4
|
+
|
|
5
|
+
export function initSentry(): void {
|
|
6
|
+
const dsn = BUNDLED_DSN || process.env.SENTRY_DSN;
|
|
7
|
+
if (!dsn) return;
|
|
8
|
+
Sentry.init({
|
|
9
|
+
dsn,
|
|
10
|
+
release: `hq-cli@${process.env.npm_package_version ?? "0.0.0"}`,
|
|
11
|
+
environment: process.env.HQ_CLI_ENV ?? "production",
|
|
12
|
+
initialScope: {
|
|
13
|
+
tags: { repo: "hq-cli" },
|
|
14
|
+
},
|
|
15
|
+
beforeSend,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export { Sentry };
|
|
@@ -17,7 +17,7 @@ function ensureCacheDir(companyUid: string): void {
|
|
|
17
17
|
|
|
18
18
|
function validateInputs(companyUid: string, name: string): boolean {
|
|
19
19
|
if (!companyUid || companyUid.includes("/") || companyUid.includes("..")) return false;
|
|
20
|
-
if (!/^[A-Z][A-Z0-9_]*$/.test(name)) return false;
|
|
20
|
+
if (!/^[A-Z][A-Z0-9_]*(?:\/[A-Z][A-Z0-9_]+)*$/.test(name)) return false;
|
|
21
21
|
return true;
|
|
22
22
|
}
|
|
23
23
|
|
|
@@ -99,6 +99,7 @@ export function writeCache(companyUid: string, name: string, value: string): voi
|
|
|
99
99
|
|
|
100
100
|
const out = Buffer.concat([timestamp, iv, authTag, encrypted]);
|
|
101
101
|
const filePath = path.join(CACHE_DIR, companyUid, name);
|
|
102
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true, mode: 0o700 });
|
|
102
103
|
const tmpPath = `${filePath}.tmp.${process.pid}`;
|
|
103
104
|
fs.writeFileSync(tmpPath, out, { mode: 0o600 });
|
|
104
105
|
fs.renameSync(tmpPath, filePath);
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hq-auth-refresh.d.ts","sourceRoot":"","sources":["../../src/bin/hq-auth-refresh.ts"],"names":[],"mappings":";AAEA;;;;;;;;;GASG"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hq-auth-refresh.js","sourceRoot":"","sources":["../../src/bin/hq-auth-refresh.ts"],"names":[],"mappings":";AAEA;;;;;;;;;GASG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAEnE,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,MAAM,oBAAoB,EAAE,CAAC;IAC5C,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;QACrB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,oBAAoB,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC;IAC9D,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,oBAAoB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CACzE,CAAC;IACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../../src/commands/add.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAkEzD"}
|
package/dist/commands/add.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"add.js","sourceRoot":"","sources":["../../src/commands/add.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAsCpC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA+F3D"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,gBAAgB,EAChB,UAAU,EACV,gBAAgB,GACjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,eAAe,EACf,oBAAoB,GACrB,MAAM,6BAA6B,CAAC;AAErC;;;;GAIG;AACH,SAAS,WAAW,CAClB,OAAe;IAEf,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACtC,IAAI,CAAC,OAAO;YAAE,OAAO,EAAE,CAAC;QACxB,MAAM,GAAG,GACP,OAAO,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;QACvE,MAAM,UAAU,GACd,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC;QACtD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CACxB,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CACpD,CAAC;QACF,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC;IACpD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,OAAgB;IACnD,MAAM,OAAO,GAAG,OAAO;SACpB,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,qCAAqC,CAAC,CAAC;IAEtD,OAAO;SACJ,OAAO,CAAC,OAAO,CAAC;SAChB,WAAW,CACV,uEAAuE,CACxE;SACA,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,MAAM,QAAQ,GAAG,gBAAgB,EAAE,CAAC;QACpC,IAAI,QAAQ,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,GAAG,CAAC,EAAE,CAAC;YAC3C,MAAM,GAAG,GAAG,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,KAAK,IAAI,gBAAgB,CAAC;YACpE,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,KAAK,CAAC,sBAAsB,GAAG,+CAA+C,CAAC,CACtF,CAAC;YACF,OAAO;QACT,CAAC;QACD,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,eAAe,CAAC,CAAC;YACnD,MAAM,GAAG,GAAG,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC;YACtD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,gBAAgB,GAAG,EAAE,CAAC,CAAC,CAAC;YAChD,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,GAAG,CAAC,wDAAwD,MAAM,CAAC,SAAS,GAAG,CAAC,CACvF,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,GAAG,GACP,GAAG,YAAY,gBAAgB;gBAC7B,CAAC,CAAC,GAAG,CAAC,OAAO;gBACb,CAAC,CAAC,GAAG,YAAY,KAAK;oBACpB,CAAC,CAAC,GAAG,CAAC,OAAO;oBACb,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACpB,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,iBAAiB,GAAG,EAAE,CAAC,CAAC,CAAC;YACjD,OAAO,CAAC,KAAK,CACX,KAAK,CAAC,GAAG,CACP,8EAA8E,CAC/E,CACF,CAAC;YACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;IAEL,OAAO;SACJ,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,qCAAqC,CAAC;SAClD,MAAM,CAAC,GAAG,EAAE;QACX,MAAM,QAAQ,GAAG,gBAAgB,EAAE,CAAC;QACpC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC,CAAC;YAClD,OAAO;QACT,CAAC;QACD,iBAAiB,EAAE,CAAC;QACpB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,gDAAgD,CAAC,CAAC,CAAC;IAC7E,CAAC,CAAC,CAAC;IAEL,OAAO;SACJ,OAAO,CAAC,SAAS,CAAC;SAClB,WAAW,CACV,mEAAmE,CACpE;SACA,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,MAAM,MAAM,GAAG,MAAM,oBAAoB,EAAE,CAAC;QAC5C,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;YACrB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC,CAAC;YACjD,OAAO;QACT,CAAC;QACD,OAAO,CAAC,KAAK,CACX,KAAK,CAAC,MAAM,CAAC,eAAe,MAAM,CAAC,MAAM,IAAI,SAAS,EAAE,CAAC,CAC1D,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;IAEL,OAAO;SACJ,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,2CAA2C,CAAC;SACxD,MAAM,CAAC,GAAG,EAAE;QACX,MAAM,MAAM,GAAG,gBAAgB,EAAE,CAAC;QAClC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,4CAA4C,CAAC,CAAC,CAAC;YACxE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,MAAM,GAAG,GAAG,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC;QAC9C,MAAM,QAAQ,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;QACpC,MAAM,KAAK,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QACrC,OAAO,CAAC,GAAG,CACT,QAAQ;YACN,CAAC,CAAC,KAAK,CAAC,MAAM,CACV,GAAG,KAAK,6CAA6C,MAAM,CAAC,SAAS,GAAG,CACzE;YACH,CAAC,CAAC,KAAK,CAAC,KAAK,CACT,GAAG,KAAK,+BAA+B,MAAM,CAAC,SAAS,GAAG,CAC3D,CACN,CAAC;IACJ,CAAC,CAAC,CAAC;AACP,CAAC"}
|