toolcraft 0.0.26 → 0.0.27
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/error-report.js
CHANGED
|
@@ -116,6 +116,25 @@ function redactValue(value) {
|
|
|
116
116
|
}
|
|
117
117
|
return `<set, ${value.length} chars>`;
|
|
118
118
|
}
|
|
119
|
+
function collectStringLeaves(value, output) {
|
|
120
|
+
if (typeof value === "string") {
|
|
121
|
+
if (value.length > 0) {
|
|
122
|
+
output.add(value);
|
|
123
|
+
}
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
if (Array.isArray(value)) {
|
|
127
|
+
for (const entry of value) {
|
|
128
|
+
collectStringLeaves(entry, output);
|
|
129
|
+
}
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (isPlainObject(value)) {
|
|
133
|
+
for (const entry of Object.values(value)) {
|
|
134
|
+
collectStringLeaves(entry, output);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
119
138
|
function schemaSecretValue(schema) {
|
|
120
139
|
const unwrapped = unwrapOptional(schema);
|
|
121
140
|
if (unwrapped.kind === "string" || unwrapped.kind === "number") {
|
|
@@ -155,6 +174,52 @@ function redactParams(params, command) {
|
|
|
155
174
|
}
|
|
156
175
|
return redactParamsValue(params, command.params, "");
|
|
157
176
|
}
|
|
177
|
+
function collectSensitiveParamValues(value, schema, name, output) {
|
|
178
|
+
if (shouldRedactParam(name, schema)) {
|
|
179
|
+
collectStringLeaves(value, output);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
const unwrapped = unwrapOptional(schema);
|
|
183
|
+
if (unwrapped.kind === "object" && isPlainObject(value)) {
|
|
184
|
+
for (const [key, childValue] of Object.entries(value)) {
|
|
185
|
+
const childSchema = unwrapped.shape[key];
|
|
186
|
+
if (childSchema !== undefined) {
|
|
187
|
+
collectSensitiveParamValues(childValue, childSchema, key, output);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
if (unwrapped.kind === "array" && Array.isArray(value)) {
|
|
193
|
+
for (const entry of value) {
|
|
194
|
+
collectSensitiveParamValues(entry, unwrapped.item, name, output);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
function createReportStringRedactor(context, env) {
|
|
199
|
+
const values = new Set();
|
|
200
|
+
for (const value of Object.values(context.secrets ?? {})) {
|
|
201
|
+
if (value !== undefined && value.length > 0) {
|
|
202
|
+
values.add(value);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
for (const [name, secret] of Object.entries(context.command?.secrets ?? {})) {
|
|
206
|
+
const value = context.secrets?.[name] ?? env[secret.env];
|
|
207
|
+
if (value !== undefined && value.length > 0) {
|
|
208
|
+
values.add(value);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
if (context.command !== undefined) {
|
|
212
|
+
collectSensitiveParamValues(context.params, context.command.params, "", values);
|
|
213
|
+
}
|
|
214
|
+
const orderedValues = [...values].sort((left, right) => right.length - left.length);
|
|
215
|
+
return (value) => {
|
|
216
|
+
let redacted = value;
|
|
217
|
+
for (const secretValue of orderedValues) {
|
|
218
|
+
redacted = redacted.split(secretValue).join("<redacted>");
|
|
219
|
+
}
|
|
220
|
+
return redacted;
|
|
221
|
+
};
|
|
222
|
+
}
|
|
158
223
|
function commandSecretEnvNames(secrets) {
|
|
159
224
|
if (secrets === undefined) {
|
|
160
225
|
return [];
|
|
@@ -203,7 +268,7 @@ function redactArgv(argv, options) {
|
|
|
203
268
|
function stableJson(value) {
|
|
204
269
|
return JSON.stringify(value, null, 2) ?? "undefined";
|
|
205
270
|
}
|
|
206
|
-
function redactStructuredErrorField(name, value) {
|
|
271
|
+
function redactStructuredErrorField(name, value, redactString) {
|
|
207
272
|
if (typeof value === "string") {
|
|
208
273
|
const redactedHeaderValue = redactHttpHeaderValue(name, value);
|
|
209
274
|
if (redactedHeaderValue !== value) {
|
|
@@ -212,23 +277,27 @@ function redactStructuredErrorField(name, value) {
|
|
|
212
277
|
if (isSensitiveName(name)) {
|
|
213
278
|
return "<redacted>";
|
|
214
279
|
}
|
|
280
|
+
return redactString(value);
|
|
215
281
|
}
|
|
216
282
|
if (Array.isArray(value)) {
|
|
217
|
-
return value.map((entry) => redactStructuredErrorField(name, entry));
|
|
283
|
+
return value.map((entry) => redactStructuredErrorField(name, entry, redactString));
|
|
218
284
|
}
|
|
219
285
|
if (isPlainObject(value)) {
|
|
220
|
-
return Object.fromEntries(Object.entries(value).map(([key, entry]) => [
|
|
286
|
+
return Object.fromEntries(Object.entries(value).map(([key, entry]) => [
|
|
287
|
+
key,
|
|
288
|
+
redactStructuredErrorField(key, entry, redactString)
|
|
289
|
+
]));
|
|
221
290
|
}
|
|
222
291
|
return value;
|
|
223
292
|
}
|
|
224
|
-
function ownStructuredFields(error) {
|
|
293
|
+
function ownStructuredFields(error, redactString) {
|
|
225
294
|
const fields = {};
|
|
226
295
|
for (const key of Object.keys(error)) {
|
|
227
296
|
if (key === "name" || key === "message" || key === "stack" || key === "cause") {
|
|
228
297
|
continue;
|
|
229
298
|
}
|
|
230
299
|
Object.defineProperty(fields, key, {
|
|
231
|
-
value: redactStructuredErrorField(key, error[key]),
|
|
300
|
+
value: redactStructuredErrorField(key, error[key], redactString),
|
|
232
301
|
enumerable: true,
|
|
233
302
|
configurable: true,
|
|
234
303
|
writable: true
|
|
@@ -236,47 +305,47 @@ function ownStructuredFields(error) {
|
|
|
236
305
|
}
|
|
237
306
|
return fields;
|
|
238
307
|
}
|
|
239
|
-
function formatStackChain(error) {
|
|
308
|
+
function formatStackChain(error, redactString) {
|
|
240
309
|
const lines = [];
|
|
241
310
|
let current = error;
|
|
242
311
|
let index = 0;
|
|
243
312
|
while (current !== undefined) {
|
|
244
313
|
if (current instanceof Error) {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
: `Caused by: ${current.stack ?? String(current)}`);
|
|
314
|
+
const stack = current.stack ?? String(current);
|
|
315
|
+
lines.push(redactString(index === 0 ? stack : `Caused by: ${stack}`));
|
|
248
316
|
current = current.cause;
|
|
249
317
|
}
|
|
250
318
|
else {
|
|
251
|
-
|
|
319
|
+
const message = String(current);
|
|
320
|
+
lines.push(redactString(index === 0 ? message : `Caused by: ${message}`));
|
|
252
321
|
current = undefined;
|
|
253
322
|
}
|
|
254
323
|
index += 1;
|
|
255
324
|
}
|
|
256
325
|
return lines.join("\n");
|
|
257
326
|
}
|
|
258
|
-
function formatHeaderValue(name, value) {
|
|
259
|
-
return redactHttpHeaderValue(name, value);
|
|
327
|
+
function formatHeaderValue(name, value, redactString) {
|
|
328
|
+
return redactString(redactHttpHeaderValue(name, value));
|
|
260
329
|
}
|
|
261
|
-
function formatHeaders(headers) {
|
|
330
|
+
function formatHeaders(headers, redactString) {
|
|
262
331
|
return Object.entries(headers)
|
|
263
|
-
.map(([name, value]) => `${name}: ${formatHeaderValue(name, value)}`)
|
|
332
|
+
.map(([name, value]) => `${name}: ${formatHeaderValue(name, value, redactString)}`)
|
|
264
333
|
.join("\n");
|
|
265
334
|
}
|
|
266
|
-
function formatBody(body) {
|
|
335
|
+
function formatBody(body, redactString) {
|
|
267
336
|
const redactedBody = redactHttpBody(body);
|
|
268
337
|
if (typeof redactedBody === "string") {
|
|
269
|
-
return redactedBody;
|
|
338
|
+
return redactString(redactedBody);
|
|
270
339
|
}
|
|
271
|
-
return stableJson(redactedBody);
|
|
340
|
+
return redactString(stableJson(redactedBody));
|
|
272
341
|
}
|
|
273
|
-
function formatHttpTranscript(error) {
|
|
342
|
+
function formatHttpTranscript(error, redactString) {
|
|
274
343
|
const requestLines = [
|
|
275
344
|
`${error.request.method} ${error.request.url}`,
|
|
276
|
-
formatHeaders(error.request.headers)
|
|
345
|
+
formatHeaders(error.request.headers, redactString)
|
|
277
346
|
].filter((line) => line.length > 0);
|
|
278
347
|
if (error.request.body !== undefined) {
|
|
279
|
-
requestLines.push("", formatBody(error.request.body));
|
|
348
|
+
requestLines.push("", formatBody(error.request.body, redactString));
|
|
280
349
|
}
|
|
281
350
|
return [
|
|
282
351
|
"Request:",
|
|
@@ -284,9 +353,9 @@ function formatHttpTranscript(error) {
|
|
|
284
353
|
"",
|
|
285
354
|
"Response:",
|
|
286
355
|
`${error.response.status} ${error.response.statusText}`,
|
|
287
|
-
formatHeaders(error.response.headers),
|
|
356
|
+
formatHeaders(error.response.headers, redactString),
|
|
288
357
|
"",
|
|
289
|
-
formatBody(error.response.body)
|
|
358
|
+
formatBody(error.response.body, redactString)
|
|
290
359
|
].join("\n");
|
|
291
360
|
}
|
|
292
361
|
function resolveToolcraftVersion(version) {
|
|
@@ -297,9 +366,10 @@ function resolveToolcraftVersion(version) {
|
|
|
297
366
|
function buildReport(context) {
|
|
298
367
|
const env = context.env ?? process.env;
|
|
299
368
|
const error = context.error;
|
|
369
|
+
const redactString = createReportStringRedactor(context, env);
|
|
300
370
|
const errorName = error instanceof Error ? error.name : typeof error;
|
|
301
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
302
|
-
const structuredFields = error instanceof Error ? ownStructuredFields(error) : {};
|
|
371
|
+
const errorMessage = redactString(error instanceof Error ? error.message : String(error));
|
|
372
|
+
const structuredFields = error instanceof Error ? ownStructuredFields(error, redactString) : {};
|
|
303
373
|
const secretLines = Object.entries(context.command?.secrets ?? {}).map(([name, secret]) => {
|
|
304
374
|
const value = context.secrets?.[name] ?? env[secret.env];
|
|
305
375
|
return `${secret.env}=${redactValue(value)}`;
|
|
@@ -313,7 +383,7 @@ function buildReport(context) {
|
|
|
313
383
|
`platform: ${process.platform} ${process.arch}`,
|
|
314
384
|
"",
|
|
315
385
|
"Argv",
|
|
316
|
-
stableJson(redactArgv(context.argv, { command: context.command, secrets: context.secrets })),
|
|
386
|
+
redactString(stableJson(redactArgv(context.argv, { command: context.command, secrets: context.secrets }))),
|
|
317
387
|
"",
|
|
318
388
|
"Resolved Secrets",
|
|
319
389
|
...(secretLines.length === 0 ? ["<none>"] : secretLines),
|
|
@@ -324,19 +394,19 @@ function buildReport(context) {
|
|
|
324
394
|
: context.commandPath,
|
|
325
395
|
"",
|
|
326
396
|
"Parsed Params",
|
|
327
|
-
stableJson(redactParams(context.params, context.command)),
|
|
397
|
+
redactString(stableJson(redactParams(context.params, context.command))),
|
|
328
398
|
"",
|
|
329
399
|
"Error",
|
|
330
400
|
`name: ${errorName}`,
|
|
331
401
|
`message: ${errorMessage}`,
|
|
332
402
|
"structured fields:",
|
|
333
|
-
stableJson(structuredFields),
|
|
403
|
+
redactString(stableJson(structuredFields)),
|
|
334
404
|
"",
|
|
335
405
|
"Stack",
|
|
336
|
-
formatStackChain(error)
|
|
406
|
+
formatStackChain(error, redactString)
|
|
337
407
|
];
|
|
338
408
|
if (hasHttpContext(error)) {
|
|
339
|
-
lines.push("", "HTTP Transcript", formatHttpTranscript(error));
|
|
409
|
+
lines.push("", "HTTP Transcript", formatHttpTranscript(error, redactString));
|
|
340
410
|
}
|
|
341
411
|
return `${lines.join("\n")}\n`;
|
|
342
412
|
}
|
|
@@ -126,6 +126,14 @@ function buildVerifyReport(lookup, opts) {
|
|
|
126
126
|
export async function syncGhProject(opts) {
|
|
127
127
|
const client = resolveGhClient(opts);
|
|
128
128
|
let lookup = await lookupProject(client, opts.owner, opts.number);
|
|
129
|
+
const initialReport = buildVerifyReport(lookup, opts);
|
|
130
|
+
if (initialReport.ok || opts.yes !== true) {
|
|
131
|
+
return {
|
|
132
|
+
...initialReport,
|
|
133
|
+
created: [],
|
|
134
|
+
updated: []
|
|
135
|
+
};
|
|
136
|
+
}
|
|
129
137
|
let resolvedNumber = opts.number;
|
|
130
138
|
const created = [];
|
|
131
139
|
if (lookup.project === null) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "toolcraft",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.27",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@clack/core": "^1.0.0",
|
|
47
47
|
"@clack/prompts": "^1.0.0",
|
|
48
|
-
"toolcraft-schema": "0.0.
|
|
48
|
+
"toolcraft-schema": "0.0.27",
|
|
49
49
|
"commander": "^14.0.3",
|
|
50
50
|
"jose": "^6.1.2",
|
|
51
51
|
"jsonc-parser": "^3.3.1",
|