securewithtrace 0.20.1 → 0.20.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/index.js +142 -15
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -26173,6 +26173,7 @@ function colorAccentUnderline(text, colorizer = source_default) {
|
|
|
26173
26173
|
var readTokenSafe = readToken;
|
|
26174
26174
|
var PRODUCTION_API_URL = "https://api.securewithtrace.com";
|
|
26175
26175
|
var UNAUTHORIZED_STATUS = 401;
|
|
26176
|
+
var REFRESH_AUTH_FAILURE_STATUS_CODES = /* @__PURE__ */ new Set([400, 401]);
|
|
26176
26177
|
function getApiBaseUrl() {
|
|
26177
26178
|
const raw = process.env["TRACE_API_URL"];
|
|
26178
26179
|
if (!raw) {
|
|
@@ -26217,9 +26218,24 @@ function getWorkosClientId() {
|
|
|
26217
26218
|
}
|
|
26218
26219
|
return clientId;
|
|
26219
26220
|
}
|
|
26221
|
+
function parseRefreshFailureStatus(error48) {
|
|
26222
|
+
if (!(error48 instanceof Error)) {
|
|
26223
|
+
return null;
|
|
26224
|
+
}
|
|
26225
|
+
const statusMatch = error48.message.match(/\b([1-5]\d{2})\b/);
|
|
26226
|
+
if (!statusMatch?.[1]) {
|
|
26227
|
+
return null;
|
|
26228
|
+
}
|
|
26229
|
+
return Number.parseInt(statusMatch[1], 10);
|
|
26230
|
+
}
|
|
26231
|
+
function isRefreshAuthFailure(error48) {
|
|
26232
|
+
const status = parseRefreshFailureStatus(error48);
|
|
26233
|
+
return status !== null && REFRESH_AUTH_FAILURE_STATUS_CODES.has(status);
|
|
26234
|
+
}
|
|
26220
26235
|
async function refreshStoredAccessToken() {
|
|
26221
26236
|
const clientId = getWorkosClientId();
|
|
26222
26237
|
const refreshToken = readRefreshToken();
|
|
26238
|
+
const tokenOrganizationId = extractOrganizationIdFromToken(readToken());
|
|
26223
26239
|
if (!clientId || !refreshToken) {
|
|
26224
26240
|
return null;
|
|
26225
26241
|
}
|
|
@@ -26229,7 +26245,20 @@ async function refreshStoredAccessToken() {
|
|
|
26229
26245
|
writeTokens(tokenResponse.access_token, nextRefreshToken);
|
|
26230
26246
|
return tokenResponse.access_token;
|
|
26231
26247
|
} catch (error48) {
|
|
26232
|
-
if (
|
|
26248
|
+
if (tokenOrganizationId && isRefreshAuthFailure(error48)) {
|
|
26249
|
+
try {
|
|
26250
|
+
const tokenResponse = await refreshAccessToken(clientId, refreshToken, tokenOrganizationId);
|
|
26251
|
+
const nextRefreshToken = tokenResponse.refresh_token ?? refreshToken;
|
|
26252
|
+
writeTokens(tokenResponse.access_token, nextRefreshToken);
|
|
26253
|
+
return tokenResponse.access_token;
|
|
26254
|
+
} catch (orgRefreshError) {
|
|
26255
|
+
if (isRefreshAuthFailure(orgRefreshError)) {
|
|
26256
|
+
removeToken();
|
|
26257
|
+
}
|
|
26258
|
+
return null;
|
|
26259
|
+
}
|
|
26260
|
+
}
|
|
26261
|
+
if (isRefreshAuthFailure(error48)) {
|
|
26233
26262
|
removeToken();
|
|
26234
26263
|
}
|
|
26235
26264
|
return null;
|
|
@@ -26247,7 +26276,7 @@ async function refreshStoredAccessTokenForOrganization(workosOrganizationId) {
|
|
|
26247
26276
|
writeTokens(tokenResponse.access_token, nextRefreshToken);
|
|
26248
26277
|
return true;
|
|
26249
26278
|
} catch (error48) {
|
|
26250
|
-
if (
|
|
26279
|
+
if (isRefreshAuthFailure(error48)) {
|
|
26251
26280
|
removeToken();
|
|
26252
26281
|
}
|
|
26253
26282
|
return false;
|
|
@@ -26383,6 +26412,32 @@ async function resolveOrganizationId(client) {
|
|
|
26383
26412
|
}
|
|
26384
26413
|
return id;
|
|
26385
26414
|
}
|
|
26415
|
+
async function resolveOrganizationSlug() {
|
|
26416
|
+
const result = await authenticatedRequest((client) => client.organization.listMyOrganizations());
|
|
26417
|
+
if (result.status === 401) {
|
|
26418
|
+
console.error(
|
|
26419
|
+
colorError("Authentication failed. Run `tracecli auth login` to re-authenticate.")
|
|
26420
|
+
);
|
|
26421
|
+
process.exit(1);
|
|
26422
|
+
}
|
|
26423
|
+
if (result.status !== 200) {
|
|
26424
|
+
console.error(colorError(`Failed to resolve organization (status ${result.status}).`));
|
|
26425
|
+
process.exit(1);
|
|
26426
|
+
}
|
|
26427
|
+
const organizations = result.body;
|
|
26428
|
+
const selectedOrganizationId = readSelectedOrganizationId();
|
|
26429
|
+
const tokenOrganizationId = extractOrganizationIdFromToken(readToken());
|
|
26430
|
+
const activeOrganization = determineActiveOrganization(
|
|
26431
|
+
organizations,
|
|
26432
|
+
selectedOrganizationId,
|
|
26433
|
+
tokenOrganizationId
|
|
26434
|
+
);
|
|
26435
|
+
if (!activeOrganization) {
|
|
26436
|
+
console.error(colorError("No organization found for your account."));
|
|
26437
|
+
process.exit(1);
|
|
26438
|
+
}
|
|
26439
|
+
return activeOrganization.organization.slug;
|
|
26440
|
+
}
|
|
26386
26441
|
async function resolveOrganizationIdSafe(client) {
|
|
26387
26442
|
try {
|
|
26388
26443
|
const result = client ? await client.organization.listMyOrganizations() : await authenticatedRequestSafe(
|
|
@@ -26502,6 +26557,10 @@ function isSafeOrganizationId(organizationId) {
|
|
|
26502
26557
|
}
|
|
26503
26558
|
async function fetchOrganizationContext() {
|
|
26504
26559
|
const result = await authenticatedRequest((client) => client.organization.listMyOrganizations());
|
|
26560
|
+
if (result.status === 401) {
|
|
26561
|
+
console.error("Authentication failed. Run `tracecli auth login` to re-authenticate.");
|
|
26562
|
+
process.exit(1);
|
|
26563
|
+
}
|
|
26505
26564
|
if (result.status !== 200) {
|
|
26506
26565
|
console.error(`Failed to list organizations (status ${result.status}).`);
|
|
26507
26566
|
process.exit(1);
|
|
@@ -26828,22 +26887,30 @@ Examples:
|
|
|
26828
26887
|
});
|
|
26829
26888
|
}
|
|
26830
26889
|
function createOrgListCommand() {
|
|
26831
|
-
return new Command("list").description("List organizations available to your account").option("-o, --output <format>", "Output format: table (default), json, tsv").
|
|
26890
|
+
return new Command("list").description("List organizations available to your account").option("-o, --output <format>", "Output format: table (default), json, tsv").option("-L, --limit <n>", "Max organizations to display (default: all)", (v) => {
|
|
26891
|
+
const n = Number.parseInt(v, 10);
|
|
26892
|
+
if (Number.isNaN(n) || n < 1) {
|
|
26893
|
+
throw new Error("--limit must be a positive integer");
|
|
26894
|
+
}
|
|
26895
|
+
return n;
|
|
26896
|
+
}).addHelpText(
|
|
26832
26897
|
"after",
|
|
26833
26898
|
`
|
|
26834
26899
|
Examples:
|
|
26835
26900
|
tracecli org list
|
|
26836
26901
|
tracecli org list --output json
|
|
26837
|
-
tracecli org list --output tsv | cut -f1
|
|
26902
|
+
tracecli org list --output tsv | cut -f1
|
|
26903
|
+
tracecli org list --limit 5`
|
|
26838
26904
|
).action(async (options) => {
|
|
26839
26905
|
const organizationContext = await fetchOrganizationContext();
|
|
26840
|
-
const
|
|
26906
|
+
const allItems = organizationContext.organizations.map(
|
|
26841
26907
|
(organization) => ({
|
|
26842
26908
|
...organization,
|
|
26843
26909
|
active: organization.organizationId === organizationContext.activeOrganization.organizationId,
|
|
26844
26910
|
selected: organization.organizationId === organizationContext.selectedOrganizationId
|
|
26845
26911
|
})
|
|
26846
26912
|
);
|
|
26913
|
+
const items = options.limit !== void 0 ? allItems.slice(0, options.limit) : allItems;
|
|
26847
26914
|
if (options.output === "json") {
|
|
26848
26915
|
console.log(JSON.stringify(items, null, 2));
|
|
26849
26916
|
return;
|
|
@@ -26862,15 +26929,27 @@ Examples:
|
|
|
26862
26929
|
}
|
|
26863
26930
|
return;
|
|
26864
26931
|
}
|
|
26865
|
-
|
|
26866
|
-
|
|
26932
|
+
const headers = ["", "Name", "Slug", "Org ID"];
|
|
26933
|
+
const rows = items.map((item) => {
|
|
26867
26934
|
const labels = [item.active ? "active" : null, item.selected ? "selected" : null].filter(
|
|
26868
26935
|
Boolean
|
|
26869
26936
|
);
|
|
26870
|
-
const
|
|
26871
|
-
|
|
26872
|
-
|
|
26873
|
-
|
|
26937
|
+
const marker = item.active ? "*" : " ";
|
|
26938
|
+
const nameSuffix = labels.length > 0 ? ` [${labels.join(", ")}]` : "";
|
|
26939
|
+
return [
|
|
26940
|
+
marker,
|
|
26941
|
+
sanitizeForOutput(item.name) + nameSuffix,
|
|
26942
|
+
sanitizeForOutput(item.slug),
|
|
26943
|
+
item.organizationId
|
|
26944
|
+
];
|
|
26945
|
+
});
|
|
26946
|
+
const colWidths = headers.map(
|
|
26947
|
+
(h, i) => Math.max(h.length, ...rows.map((r) => (r[i] ?? "").length))
|
|
26948
|
+
);
|
|
26949
|
+
const pad = (s, w3) => s.padEnd(w3);
|
|
26950
|
+
console.log(headers.map((h, i) => pad(h, colWidths[i] ?? h.length)).join(" "));
|
|
26951
|
+
for (const row of rows) {
|
|
26952
|
+
console.log(row.map((cell, i) => pad(cell, colWidths[i] ?? 0)).join(" "));
|
|
26874
26953
|
}
|
|
26875
26954
|
if (organizationContext.staleSelectionDetected) {
|
|
26876
26955
|
console.log("\nYour previous organization selection is no longer available and was reset.");
|
|
@@ -27698,6 +27777,41 @@ function createPentestCommand() {
|
|
|
27698
27777
|
import { chmodSync, mkdtempSync, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
27699
27778
|
import { tmpdir } from "os";
|
|
27700
27779
|
import { join as join4 } from "path";
|
|
27780
|
+
|
|
27781
|
+
// src/browser.ts
|
|
27782
|
+
var DEFAULT_APP_BASE_URL = "https://app.securewithtrace.com";
|
|
27783
|
+
function getAppBaseUrl() {
|
|
27784
|
+
const override = process.env["TRACE_APP_URL"];
|
|
27785
|
+
if (typeof override === "string" && override.trim().length > 0) {
|
|
27786
|
+
try {
|
|
27787
|
+
const parsed = new URL(override);
|
|
27788
|
+
if (parsed.protocol === "https:") {
|
|
27789
|
+
return override.replace(/\/+$/, "");
|
|
27790
|
+
}
|
|
27791
|
+
} catch {
|
|
27792
|
+
}
|
|
27793
|
+
}
|
|
27794
|
+
return DEFAULT_APP_BASE_URL;
|
|
27795
|
+
}
|
|
27796
|
+
async function openBrowser2(url2) {
|
|
27797
|
+
try {
|
|
27798
|
+
const parsed = new URL(url2);
|
|
27799
|
+
if (parsed.protocol !== "https:") {
|
|
27800
|
+
return false;
|
|
27801
|
+
}
|
|
27802
|
+
const proc = Bun.spawn(["open", url2], { stdout: "ignore", stderr: "ignore" });
|
|
27803
|
+
const exitCode = await proc.exited;
|
|
27804
|
+
if (exitCode !== 0) {
|
|
27805
|
+
const linuxProc = Bun.spawn(["xdg-open", url2], { stdout: "ignore", stderr: "ignore" });
|
|
27806
|
+
return await linuxProc.exited === 0;
|
|
27807
|
+
}
|
|
27808
|
+
return true;
|
|
27809
|
+
} catch {
|
|
27810
|
+
return false;
|
|
27811
|
+
}
|
|
27812
|
+
}
|
|
27813
|
+
|
|
27814
|
+
// src/repo/commands.ts
|
|
27701
27815
|
function stripAnsi(str) {
|
|
27702
27816
|
const ESC2 = String.fromCharCode(27);
|
|
27703
27817
|
const CSI2 = String.fromCharCode(155);
|
|
@@ -27984,7 +28098,9 @@ function printRepositoriesTable(repositories) {
|
|
|
27984
28098
|
repo.status === "CONNECTED" ? "connected" : "disconnected"
|
|
27985
28099
|
]
|
|
27986
28100
|
);
|
|
27987
|
-
const colWidths = headers.map(
|
|
28101
|
+
const colWidths = headers.map(
|
|
28102
|
+
(h, i) => Math.max(h.length, ...rows.map((r) => (r[i] ?? "").length))
|
|
28103
|
+
);
|
|
27988
28104
|
const pad = (s, w3) => s.padEnd(w3);
|
|
27989
28105
|
console.log(headers.map((h, i) => pad(h, colWidths[i] ?? h.length)).join(" "));
|
|
27990
28106
|
for (const row of rows) {
|
|
@@ -28122,9 +28238,18 @@ function addRepositoryFilterOptions(command) {
|
|
|
28122
28238
|
).option(
|
|
28123
28239
|
"--dependency <pkg>",
|
|
28124
28240
|
"Filter repositories that use a dependency (e.g. lodash, lodash@4.17.21, @types/node@20)"
|
|
28125
|
-
);
|
|
28241
|
+
).option("-w, --web", "Open the repositories dashboard in a browser");
|
|
28126
28242
|
}
|
|
28127
28243
|
async function runRepositoryListCommand(options) {
|
|
28244
|
+
if (options.web) {
|
|
28245
|
+
const slug = await resolveOrganizationSlug();
|
|
28246
|
+
const url2 = `${getAppBaseUrl()}/dashboard/${slug}/repositories`;
|
|
28247
|
+
const opened = await openBrowser2(url2);
|
|
28248
|
+
if (!opened) {
|
|
28249
|
+
console.log(`Open in browser: ${url2}`);
|
|
28250
|
+
}
|
|
28251
|
+
return;
|
|
28252
|
+
}
|
|
28128
28253
|
const organizationId = await resolveOrganizationId();
|
|
28129
28254
|
if (options.dependency) {
|
|
28130
28255
|
await runDependencyFilter(organizationId, {
|
|
@@ -28256,7 +28381,8 @@ Examples:
|
|
|
28256
28381
|
tracecli repo list --output json | jq '.[].repositoryUrl'
|
|
28257
28382
|
tracecli repo list --output tsv | cut -f1
|
|
28258
28383
|
tracecli repo list --filter "language:typescript" --limit 10
|
|
28259
|
-
tracecli repo list --dependency lodash@4.17.21
|
|
28384
|
+
tracecli repo list --dependency lodash@4.17.21
|
|
28385
|
+
tracecli repo list --web`
|
|
28260
28386
|
).action(async (options) => {
|
|
28261
28387
|
await runRepositoryListCommand(options);
|
|
28262
28388
|
});
|
|
@@ -28269,7 +28395,8 @@ function createFilterCommand() {
|
|
|
28269
28395
|
`
|
|
28270
28396
|
Examples:
|
|
28271
28397
|
tracecli repo filter --filter "platform=GITHUB" --filter "status=CONNECTED"
|
|
28272
|
-
tracecli repo filter --language typescript --output json
|
|
28398
|
+
tracecli repo filter --language typescript --output json
|
|
28399
|
+
tracecli repo filter --web`
|
|
28273
28400
|
).action(async (options) => {
|
|
28274
28401
|
await runRepositoryListCommand(options);
|
|
28275
28402
|
});
|