azdo-cli 0.22.0 → 0.23.0-develop.899
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.
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/services/oauth-token-refresh.ts
|
|
4
|
-
import { closeSync, mkdirSync, openSync, unlinkSync } from "fs";
|
|
4
|
+
import { closeSync as closeSync2, mkdirSync, openSync as openSync2, unlinkSync } from "fs";
|
|
5
5
|
import { homedir } from "os";
|
|
6
6
|
import { join } from "path";
|
|
7
7
|
|
|
@@ -37,12 +37,12 @@ import os from "os";
|
|
|
37
37
|
|
|
38
38
|
// src/services/credential-store-kind.ts
|
|
39
39
|
var CREDENTIAL_STORES = ["keyring", "dpapi"];
|
|
40
|
-
function parseCredentialStore(value,
|
|
40
|
+
function parseCredentialStore(value, platform2 = process.platform) {
|
|
41
41
|
const normalised = value.trim().toLowerCase();
|
|
42
42
|
if (!CREDENTIAL_STORES.includes(normalised)) {
|
|
43
43
|
throw new Error(`Invalid value "${value}" for credentialStore. Must be one of: ${CREDENTIAL_STORES.join(", ")}.`);
|
|
44
44
|
}
|
|
45
|
-
if (normalised === "dpapi" &&
|
|
45
|
+
if (normalised === "dpapi" && platform2 !== "win32") {
|
|
46
46
|
throw new Error('credentialStore "dpapi" is only available on Windows.');
|
|
47
47
|
}
|
|
48
48
|
return normalised;
|
|
@@ -349,14 +349,14 @@ function buildScopeString(scopes) {
|
|
|
349
349
|
|
|
350
350
|
// src/services/browser-open.ts
|
|
351
351
|
import { execFile } from "child_process";
|
|
352
|
-
function isHeadless(
|
|
353
|
-
if (
|
|
352
|
+
function isHeadless(platform2, hasDisplay) {
|
|
353
|
+
if (platform2 === "linux") {
|
|
354
354
|
return !hasDisplay;
|
|
355
355
|
}
|
|
356
356
|
return false;
|
|
357
357
|
}
|
|
358
|
-
function commandForPlatform(
|
|
359
|
-
switch (
|
|
358
|
+
function commandForPlatform(platform2) {
|
|
359
|
+
switch (platform2) {
|
|
360
360
|
case "darwin":
|
|
361
361
|
return { cmd: "open", args: (url) => [url] };
|
|
362
362
|
case "win32":
|
|
@@ -368,15 +368,15 @@ function commandForPlatform(platform) {
|
|
|
368
368
|
}
|
|
369
369
|
}
|
|
370
370
|
async function openUrl(url, opts = {}) {
|
|
371
|
-
const
|
|
371
|
+
const platform2 = opts.platform ?? process.platform;
|
|
372
372
|
const hasDisplay = opts.hasDisplay ?? (process.env.DISPLAY !== void 0 && process.env.DISPLAY !== "");
|
|
373
373
|
const forcePrint = opts.forcePrint ?? false;
|
|
374
|
-
if (forcePrint || isHeadless(
|
|
374
|
+
if (forcePrint || isHeadless(platform2, hasDisplay)) {
|
|
375
375
|
process.stderr.write(`Open this URL in your browser: ${url}
|
|
376
376
|
`);
|
|
377
377
|
return "printed";
|
|
378
378
|
}
|
|
379
|
-
const spec = commandForPlatform(
|
|
379
|
+
const spec = commandForPlatform(platform2);
|
|
380
380
|
if (!spec) {
|
|
381
381
|
process.stderr.write(`Open this URL in your browser: ${url}
|
|
382
382
|
`);
|
|
@@ -780,9 +780,110 @@ function readAuditEvents() {
|
|
|
780
780
|
// src/services/credential-store.ts
|
|
781
781
|
import { Entry } from "@napi-rs/keyring";
|
|
782
782
|
|
|
783
|
+
// src/services/trace-writer.ts
|
|
784
|
+
import { openSync, writeSync, closeSync } from "fs";
|
|
785
|
+
import { platform } from "os";
|
|
786
|
+
var REDACTED = "[REDACTED]";
|
|
787
|
+
var SENSITIVE_HEADER = /^(authorization|x-.*token)$/i;
|
|
788
|
+
var SENSITIVE_QUERY_PARAM = /^(token|pat)$/i;
|
|
789
|
+
var SENSITIVE_BODY_FIELD = /^(token|accessToken|pat)$/;
|
|
790
|
+
function redactHeaders(headers) {
|
|
791
|
+
const out = {};
|
|
792
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
793
|
+
out[key] = SENSITIVE_HEADER.test(key) ? REDACTED : value;
|
|
794
|
+
}
|
|
795
|
+
return out;
|
|
796
|
+
}
|
|
797
|
+
function redactUrl(url) {
|
|
798
|
+
try {
|
|
799
|
+
const u = new URL(url);
|
|
800
|
+
for (const [key] of u.searchParams.entries()) {
|
|
801
|
+
if (SENSITIVE_QUERY_PARAM.test(key)) {
|
|
802
|
+
u.searchParams.set(key, REDACTED);
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
return u.toString();
|
|
806
|
+
} catch {
|
|
807
|
+
return url;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
var SECRET_ASSIGNMENT = String.raw`["']?\s*[:=]\s*["']?)[^"'\s,;&}]+`;
|
|
811
|
+
var SECRET_IN_TEXT = [
|
|
812
|
+
[/\b(Bearer|Basic)\s+[\w.~+/=-]{8,}/gi, `$1 ${REDACTED}`],
|
|
813
|
+
[new RegExp(String.raw`(\b(?:access|refresh|api)[_-]?(?:token|key)\b${SECRET_ASSIGNMENT}`, "gi"), `$1${REDACTED}`],
|
|
814
|
+
[new RegExp(String.raw`(\b(?:token|pat|password|secret)\b${SECRET_ASSIGNMENT}`, "gi"), `$1${REDACTED}`],
|
|
815
|
+
[/\beyJ[A-Za-z0-9._~+/=-]{20,}/g, REDACTED],
|
|
816
|
+
[/\b(?=[A-Za-z0-9]*\d)(?=[A-Za-z0-9]*[A-Za-z])[A-Za-z0-9]{40,}\b/g, REDACTED]
|
|
817
|
+
];
|
|
818
|
+
function redactText(text) {
|
|
819
|
+
let out = text;
|
|
820
|
+
for (const [pattern, replacement] of SECRET_IN_TEXT) {
|
|
821
|
+
out = out.replace(pattern, replacement);
|
|
822
|
+
}
|
|
823
|
+
return out;
|
|
824
|
+
}
|
|
825
|
+
function redactBody(body) {
|
|
826
|
+
if (body === null) return null;
|
|
827
|
+
try {
|
|
828
|
+
const parsed = JSON.parse(body);
|
|
829
|
+
let changed = false;
|
|
830
|
+
const redacted = { ...parsed };
|
|
831
|
+
for (const key of Object.keys(parsed)) {
|
|
832
|
+
if (SENSITIVE_BODY_FIELD.test(key)) {
|
|
833
|
+
redacted[key] = REDACTED;
|
|
834
|
+
changed = true;
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
return changed ? JSON.stringify(redacted) : body;
|
|
838
|
+
} catch {
|
|
839
|
+
return body;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
var TraceWriter = class {
|
|
843
|
+
fd;
|
|
844
|
+
constructor(filepath) {
|
|
845
|
+
const mode = platform() === "win32" ? void 0 : 384;
|
|
846
|
+
this.fd = openSync(filepath, "a", mode);
|
|
847
|
+
}
|
|
848
|
+
append(entry) {
|
|
849
|
+
const line = JSON.stringify(entry) + "\n\n";
|
|
850
|
+
writeSync(this.fd, line);
|
|
851
|
+
}
|
|
852
|
+
close() {
|
|
853
|
+
closeSync(this.fd);
|
|
854
|
+
}
|
|
855
|
+
};
|
|
856
|
+
var activeWriter = null;
|
|
857
|
+
function initTraceWriter(filepath) {
|
|
858
|
+
try {
|
|
859
|
+
activeWriter = new TraceWriter(filepath);
|
|
860
|
+
} catch (err) {
|
|
861
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
862
|
+
process.stderr.write(`Warning: could not open trace file "${filepath}": ${msg}
|
|
863
|
+
`);
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
function getActiveTraceWriter() {
|
|
867
|
+
return activeWriter;
|
|
868
|
+
}
|
|
869
|
+
|
|
783
870
|
// src/types/credential.ts
|
|
784
|
-
function
|
|
785
|
-
|
|
871
|
+
function underlyingDetail(cause) {
|
|
872
|
+
if (!(cause instanceof Error)) return null;
|
|
873
|
+
const text = redactText(cause.message).replace(/\s+/g, " ").trim();
|
|
874
|
+
if (!text) return null;
|
|
875
|
+
const name = cause.name && cause.name !== "Error" ? `${cause.name}: ` : "";
|
|
876
|
+
return `${name}${text}`;
|
|
877
|
+
}
|
|
878
|
+
function defaultUnavailableMessage(backend, cause) {
|
|
879
|
+
const detail = underlyingDetail(cause);
|
|
880
|
+
const suffix = detail ? `
|
|
881
|
+
Underlying error: ${detail}` : "";
|
|
882
|
+
if (backend === "linux-libsecret") {
|
|
883
|
+
return `OS secret backend unavailable (linux-libsecret). The Secret Service may be missing or unreachable (for example a sandbox or container blocking the user D-Bus socket, or no session bus).${suffix}
|
|
884
|
+
Install/start a Secret Service (see docs/linux-credential-store.md), run azdo outside the sandbox, or set AZDO_PAT to bypass the keyring.`;
|
|
885
|
+
}
|
|
886
|
+
const base = `OS secret backend unavailable (${backend}). Install the platform's credential service and try again.${suffix}`;
|
|
786
887
|
if (backend !== "windows-credential-manager") return base;
|
|
787
888
|
return `${base}
|
|
788
889
|
Over SSH, Windows Credential Manager is not reachable. Store credentials with DPAPI in ~/.azdo instead: \`azdo config set credentialStore dpapi\` (or AZDO_CREDENTIAL_STORE=dpapi), then \`azdo auth login\` again.`;
|
|
@@ -790,7 +891,7 @@ Over SSH, Windows Credential Manager is not reachable. Store credentials with DP
|
|
|
790
891
|
var CredentialStoreUnavailableError = class extends Error {
|
|
791
892
|
backend;
|
|
792
893
|
constructor(backend, cause, message) {
|
|
793
|
-
super(message ?? defaultUnavailableMessage(backend));
|
|
894
|
+
super(message ?? defaultUnavailableMessage(backend, cause));
|
|
794
895
|
this.name = "CredentialStoreUnavailableError";
|
|
795
896
|
this.backend = backend;
|
|
796
897
|
if (cause instanceof Error) {
|
|
@@ -1189,7 +1290,7 @@ async function deletePat(org) {
|
|
|
1189
1290
|
}
|
|
1190
1291
|
try {
|
|
1191
1292
|
const { unlinkSync: unlinkSync2 } = await import("fs");
|
|
1192
|
-
const { lockPath: lockPath2 } = await import("./oauth-token-refresh-
|
|
1293
|
+
const { lockPath: lockPath2 } = await import("./oauth-token-refresh-RLDAAR5D.js");
|
|
1193
1294
|
unlinkSync2(lockPath2(org));
|
|
1194
1295
|
} catch {
|
|
1195
1296
|
}
|
|
@@ -1289,8 +1390,8 @@ async function defaultAcquireLock(org, waitMs = DEFAULT_LOCK_WAIT_MS) {
|
|
|
1289
1390
|
const deadline = Date.now() + waitMs;
|
|
1290
1391
|
for (; ; ) {
|
|
1291
1392
|
try {
|
|
1292
|
-
const fd =
|
|
1293
|
-
|
|
1393
|
+
const fd = openSync2(path4, "wx");
|
|
1394
|
+
closeSync2(fd);
|
|
1294
1395
|
return {
|
|
1295
1396
|
release: () => {
|
|
1296
1397
|
try {
|
|
@@ -1433,6 +1534,12 @@ function _resetInFlight() {
|
|
|
1433
1534
|
}
|
|
1434
1535
|
|
|
1435
1536
|
export {
|
|
1537
|
+
redactHeaders,
|
|
1538
|
+
redactUrl,
|
|
1539
|
+
redactText,
|
|
1540
|
+
redactBody,
|
|
1541
|
+
initTraceWriter,
|
|
1542
|
+
getActiveTraceWriter,
|
|
1436
1543
|
CredentialStoreUnavailableError,
|
|
1437
1544
|
CredentialMissingError,
|
|
1438
1545
|
CredentialRefreshError,
|
package/dist/index.js
CHANGED
|
@@ -13,10 +13,12 @@ import {
|
|
|
13
13
|
deleteOrgScope,
|
|
14
14
|
deletePat,
|
|
15
15
|
firstPartyShippedScopes,
|
|
16
|
+
getActiveTraceWriter,
|
|
16
17
|
getConfigValue,
|
|
17
18
|
getOrgScopedValue,
|
|
18
19
|
getPat,
|
|
19
20
|
getStoredCredential,
|
|
21
|
+
initTraceWriter,
|
|
20
22
|
listKeyringCredentials,
|
|
21
23
|
listOrgsWithStoredPat,
|
|
22
24
|
loadConfig,
|
|
@@ -27,6 +29,10 @@ import {
|
|
|
27
29
|
probeBackend,
|
|
28
30
|
readAuditEvents,
|
|
29
31
|
readTokenResponse,
|
|
32
|
+
redactBody,
|
|
33
|
+
redactHeaders,
|
|
34
|
+
redactText,
|
|
35
|
+
redactUrl,
|
|
30
36
|
refreshIfNeeded,
|
|
31
37
|
resolveOAuthConfig,
|
|
32
38
|
resolveScopedConfig,
|
|
@@ -39,7 +45,7 @@ import {
|
|
|
39
45
|
tokenResponseToCredential,
|
|
40
46
|
unsetConfigValue,
|
|
41
47
|
unsetOrgScopedValue
|
|
42
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-DNOOZIXA.js";
|
|
43
49
|
|
|
44
50
|
// src/program.ts
|
|
45
51
|
import { Command as Command20 } from "commander";
|
|
@@ -55,93 +61,6 @@ var version = pkg.version;
|
|
|
55
61
|
// src/commands/get-item.ts
|
|
56
62
|
import { Command } from "commander";
|
|
57
63
|
|
|
58
|
-
// src/services/trace-writer.ts
|
|
59
|
-
import { openSync, writeSync, closeSync } from "fs";
|
|
60
|
-
import { platform } from "os";
|
|
61
|
-
var REDACTED = "[REDACTED]";
|
|
62
|
-
var SENSITIVE_HEADER = /^(authorization|x-.*token)$/i;
|
|
63
|
-
var SENSITIVE_QUERY_PARAM = /^(token|pat)$/i;
|
|
64
|
-
var SENSITIVE_BODY_FIELD = /^(token|accessToken|pat)$/;
|
|
65
|
-
function redactHeaders(headers) {
|
|
66
|
-
const out = {};
|
|
67
|
-
for (const [key, value] of Object.entries(headers)) {
|
|
68
|
-
out[key] = SENSITIVE_HEADER.test(key) ? REDACTED : value;
|
|
69
|
-
}
|
|
70
|
-
return out;
|
|
71
|
-
}
|
|
72
|
-
function redactUrl(url) {
|
|
73
|
-
try {
|
|
74
|
-
const u = new URL(url);
|
|
75
|
-
for (const [key] of u.searchParams.entries()) {
|
|
76
|
-
if (SENSITIVE_QUERY_PARAM.test(key)) {
|
|
77
|
-
u.searchParams.set(key, REDACTED);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
return u.toString();
|
|
81
|
-
} catch {
|
|
82
|
-
return url;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
var SECRET_ASSIGNMENT = String.raw`["']?\s*[:=]\s*["']?)[^"'\s,;&}]+`;
|
|
86
|
-
var SECRET_IN_TEXT = [
|
|
87
|
-
[/\b(Bearer|Basic)\s+[\w.~+/=-]{8,}/gi, `$1 ${REDACTED}`],
|
|
88
|
-
[new RegExp(String.raw`(\b(?:access|refresh|api)[_-]?(?:token|key)\b${SECRET_ASSIGNMENT}`, "gi"), `$1${REDACTED}`],
|
|
89
|
-
[new RegExp(String.raw`(\b(?:token|pat|password|secret)\b${SECRET_ASSIGNMENT}`, "gi"), `$1${REDACTED}`],
|
|
90
|
-
[/\beyJ[A-Za-z0-9._~+/=-]{20,}/g, REDACTED],
|
|
91
|
-
[/\b(?=[A-Za-z0-9]*\d)(?=[A-Za-z0-9]*[A-Za-z])[A-Za-z0-9]{40,}\b/g, REDACTED]
|
|
92
|
-
];
|
|
93
|
-
function redactText(text) {
|
|
94
|
-
let out = text;
|
|
95
|
-
for (const [pattern, replacement] of SECRET_IN_TEXT) {
|
|
96
|
-
out = out.replace(pattern, replacement);
|
|
97
|
-
}
|
|
98
|
-
return out;
|
|
99
|
-
}
|
|
100
|
-
function redactBody(body) {
|
|
101
|
-
if (body === null) return null;
|
|
102
|
-
try {
|
|
103
|
-
const parsed = JSON.parse(body);
|
|
104
|
-
let changed = false;
|
|
105
|
-
const redacted = { ...parsed };
|
|
106
|
-
for (const key of Object.keys(parsed)) {
|
|
107
|
-
if (SENSITIVE_BODY_FIELD.test(key)) {
|
|
108
|
-
redacted[key] = REDACTED;
|
|
109
|
-
changed = true;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
return changed ? JSON.stringify(redacted) : body;
|
|
113
|
-
} catch {
|
|
114
|
-
return body;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
var TraceWriter = class {
|
|
118
|
-
fd;
|
|
119
|
-
constructor(filepath) {
|
|
120
|
-
const mode = platform() === "win32" ? void 0 : 384;
|
|
121
|
-
this.fd = openSync(filepath, "a", mode);
|
|
122
|
-
}
|
|
123
|
-
append(entry) {
|
|
124
|
-
const line = JSON.stringify(entry) + "\n\n";
|
|
125
|
-
writeSync(this.fd, line);
|
|
126
|
-
}
|
|
127
|
-
close() {
|
|
128
|
-
closeSync(this.fd);
|
|
129
|
-
}
|
|
130
|
-
};
|
|
131
|
-
var activeWriter = null;
|
|
132
|
-
function initTraceWriter(filepath) {
|
|
133
|
-
try {
|
|
134
|
-
activeWriter = new TraceWriter(filepath);
|
|
135
|
-
} catch (err) {
|
|
136
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
137
|
-
process.stderr.write(`Warning: could not open trace file "${filepath}": ${msg}
|
|
138
|
-
`);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
function getActiveTraceWriter() {
|
|
142
|
-
return activeWriter;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
64
|
// src/services/command-helpers.ts
|
|
146
65
|
import { existsSync, readFileSync as readFileSync2 } from "fs";
|
|
147
66
|
var EXIT_CREDENTIAL_STORE_UNAVAILABLE = 4;
|