mobbdev 1.2.33 → 1.2.35

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.
@@ -73,6 +73,12 @@ function getSdk(client, withWrapper = defaultWrapper) {
73
73
  FinalizeAIBlameInferencesUpload(variables, requestHeaders, signal) {
74
74
  return withWrapper((wrappedRequestHeaders) => client.request({ document: FinalizeAiBlameInferencesUploadDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), "FinalizeAIBlameInferencesUpload", "mutation", variables);
75
75
  },
76
+ UploadTracyRecords(variables, requestHeaders, signal) {
77
+ return withWrapper((wrappedRequestHeaders) => client.request({ document: UploadTracyRecordsDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), "UploadTracyRecords", "mutation", variables);
78
+ },
79
+ GetTracyRawDataUploadUrls(variables, requestHeaders, signal) {
80
+ return withWrapper((wrappedRequestHeaders) => client.request({ document: GetTracyRawDataUploadUrlsDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), "GetTracyRawDataUploadUrls", "mutation", variables);
81
+ },
76
82
  DigestVulnerabilityReport(variables, requestHeaders, signal) {
77
83
  return withWrapper((wrappedRequestHeaders) => client.request({ document: DigestVulnerabilityReportDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), "DigestVulnerabilityReport", "mutation", variables);
78
84
  },
@@ -126,7 +132,7 @@ function getSdk(client, withWrapper = defaultWrapper) {
126
132
  }
127
133
  };
128
134
  }
129
- var AiBlameInferenceType, FixQuestionInputType, Language, ManifestAction, Effort_To_Apply_Fix_Enum, Fix_Rating_Tag_Enum, Fix_Report_State_Enum, Fix_State_Enum, IssueLanguage_Enum, IssueType_Enum, Pr_Status_Enum, Project_Role_Type_Enum, Vulnerability_Report_Issue_Category_Enum, Vulnerability_Report_Issue_State_Enum, Vulnerability_Report_Issue_Tag_Enum, Vulnerability_Report_Vendor_Enum, Vulnerability_Severity_Enum, FixDetailsFragmentDoc, FixReportSummaryFieldsFragmentDoc, MeDocument, GetLastOrgAndNamedProjectDocument, GetLastOrgDocument, GetEncryptedApiTokenDocument, FixReportStateDocument, GetVulnerabilityReportPathsDocument, GetAnalysisSubscriptionDocument, GetAnalysisDocument, GetFixesDocument, GetVulByNodesMetadataDocument, GetFalsePositiveDocument, UpdateScmTokenDocument, UploadS3BucketInfoDocument, GetTracyDiffUploadUrlDocument, AnalyzeCommitForExtensionAiBlameDocument, GetAiBlameInferenceDocument, GetAiBlameAttributionPromptDocument, GetPromptSummaryDocument, UploadAiBlameInferencesInitDocument, FinalizeAiBlameInferencesUploadDocument, DigestVulnerabilityReportDocument, SubmitVulnerabilityReportDocument, CreateCommunityUserDocument, CreateCliLoginDocument, PerformCliLoginDocument, CreateProjectDocument, ValidateRepoUrlDocument, GitReferenceDocument, AutoPrAnalysisDocument, GetFixReportsByRepoUrlDocument, GetReportFixesDocument, GetLatestReportByRepoUrlDocument, UpdateDownloadedFixDataDocument, GetUserMvsAutoFixDocument, StreamBlameAiAnalysisRequestsDocument, StreamCommitBlameRequestsDocument, ScanSkillDocument, defaultWrapper;
135
+ var AiBlameInferenceType, FixQuestionInputType, Language, ManifestAction, Effort_To_Apply_Fix_Enum, Fix_Rating_Tag_Enum, Fix_Report_State_Enum, Fix_State_Enum, IssueLanguage_Enum, IssueType_Enum, Pr_Status_Enum, Project_Role_Type_Enum, Vulnerability_Report_Issue_Category_Enum, Vulnerability_Report_Issue_State_Enum, Vulnerability_Report_Issue_Tag_Enum, Vulnerability_Report_Vendor_Enum, Vulnerability_Severity_Enum, FixDetailsFragmentDoc, FixReportSummaryFieldsFragmentDoc, MeDocument, GetLastOrgAndNamedProjectDocument, GetLastOrgDocument, GetEncryptedApiTokenDocument, FixReportStateDocument, GetVulnerabilityReportPathsDocument, GetAnalysisSubscriptionDocument, GetAnalysisDocument, GetFixesDocument, GetVulByNodesMetadataDocument, GetFalsePositiveDocument, UpdateScmTokenDocument, UploadS3BucketInfoDocument, GetTracyDiffUploadUrlDocument, AnalyzeCommitForExtensionAiBlameDocument, GetAiBlameInferenceDocument, GetAiBlameAttributionPromptDocument, GetPromptSummaryDocument, UploadAiBlameInferencesInitDocument, FinalizeAiBlameInferencesUploadDocument, UploadTracyRecordsDocument, GetTracyRawDataUploadUrlsDocument, DigestVulnerabilityReportDocument, SubmitVulnerabilityReportDocument, CreateCommunityUserDocument, CreateCliLoginDocument, PerformCliLoginDocument, CreateProjectDocument, ValidateRepoUrlDocument, GitReferenceDocument, AutoPrAnalysisDocument, GetFixReportsByRepoUrlDocument, GetReportFixesDocument, GetLatestReportByRepoUrlDocument, UpdateDownloadedFixDataDocument, GetUserMvsAutoFixDocument, StreamBlameAiAnalysisRequestsDocument, StreamCommitBlameRequestsDocument, ScanSkillDocument, defaultWrapper;
130
136
  var init_client_generates = __esm({
131
137
  "src/features/analysis/scm/generates/client_generates.ts"() {
132
138
  "use strict";
@@ -962,6 +968,28 @@ var init_client_generates = __esm({
962
968
  status
963
969
  error
964
970
  }
971
+ }
972
+ `;
973
+ UploadTracyRecordsDocument = `
974
+ mutation UploadTracyRecords($records: [TracyRecordInput!]!) {
975
+ uploadTracyRecords(records: $records) {
976
+ status
977
+ error
978
+ }
979
+ }
980
+ `;
981
+ GetTracyRawDataUploadUrlsDocument = `
982
+ mutation GetTracyRawDataUploadUrls($recordIds: [String!]!) {
983
+ getTracyRawDataUploadUrls(recordIds: $recordIds) {
984
+ status
985
+ error
986
+ uploads {
987
+ recordId
988
+ url
989
+ uploadFieldsJSON
990
+ uploadKey
991
+ }
992
+ }
965
993
  }
966
994
  `;
967
995
  DigestVulnerabilityReportDocument = `
@@ -2227,20 +2255,20 @@ function computeCanonicalUrl(data) {
2227
2255
  } = data;
2228
2256
  switch (scmType) {
2229
2257
  case "GitHub" /* GitHub */:
2230
- return `https://${hostname}/${organization}/${repoName}`;
2258
+ return `https://${hostname}/${organization.toLowerCase()}/${repoName.toLowerCase()}`;
2231
2259
  case "GitLab" /* GitLab */:
2232
- return `https://${hostname}/${projectPath}`;
2260
+ return `https://${hostname}/${projectPath.toLowerCase()}`;
2233
2261
  case "Bitbucket" /* Bitbucket */:
2234
- return `https://${hostname}/${organization}/${repoName}`;
2262
+ return `https://${hostname}/${organization.toLowerCase()}/${repoName.toLowerCase()}`;
2235
2263
  case "Ado" /* Ado */: {
2236
2264
  const adoHostname = hostname === "ssh.dev.azure.com" ? "dev.azure.com" : hostname;
2237
2265
  if (projectName) {
2238
- return `https://${adoHostname}/${organization}/${projectName}/_git/${repoName}`;
2266
+ return `https://${adoHostname}/${organization.toLowerCase()}/${projectName.toLowerCase()}/_git/${repoName.toLowerCase()}`;
2239
2267
  }
2240
- return `https://${adoHostname}/${organization}/_git/${repoName}`;
2268
+ return `https://${adoHostname}/${organization.toLowerCase()}/_git/${repoName.toLowerCase()}`;
2241
2269
  }
2242
2270
  default:
2243
- return `https://${hostname}/${projectPath}`;
2271
+ return `https://${hostname}/${projectPath.toLowerCase()}`;
2244
2272
  }
2245
2273
  }
2246
2274
  function detectAdoUrl(args) {
@@ -4082,7 +4110,7 @@ import z27 from "zod";
4082
4110
 
4083
4111
  // src/commands/handleMobbLogin.ts
4084
4112
  import chalk2 from "chalk";
4085
- import Debug7 from "debug";
4113
+ import Debug10 from "debug";
4086
4114
 
4087
4115
  // src/utils/dirname.ts
4088
4116
  import fs from "fs";
@@ -4211,6 +4239,7 @@ var CliError = class extends Error {
4211
4239
  // src/commands/AuthManager.ts
4212
4240
  import crypto from "crypto";
4213
4241
  import os from "os";
4242
+ import Debug9 from "debug";
4214
4243
  import open from "open";
4215
4244
 
4216
4245
  // src/constants.ts
@@ -4304,7 +4333,7 @@ var VUL_REPORT_DIGEST_TIMEOUT_MS = 1e3 * 60 * 30;
4304
4333
 
4305
4334
  // src/features/analysis/graphql/gql.ts
4306
4335
  import Debug6 from "debug";
4307
- import { GraphQLClient } from "graphql-request";
4336
+ import { ClientError, GraphQLClient } from "graphql-request";
4308
4337
  import { v4 as uuidv4 } from "uuid";
4309
4338
 
4310
4339
  // src/mcp/core/Errors.ts
@@ -4374,8 +4403,7 @@ function getProxyAgent(url) {
4374
4403
  const isHttps = parsedUrl.protocol === "https:";
4375
4404
  const proxy = isHttps ? getHttpProxy() : isHttp ? getHttpProxyOnly() : null;
4376
4405
  if (proxy) {
4377
- debug2("Using proxy %s", proxy);
4378
- debug2("Proxy agent %o", proxy);
4406
+ debug2("Using proxy %s for %s", proxy, url);
4379
4407
  return new HttpsProxyAgent(proxy);
4380
4408
  }
4381
4409
  } catch (err) {
@@ -6663,6 +6691,19 @@ var GetVulByNodesMetadataZ = z26.object({
6663
6691
 
6664
6692
  // src/features/analysis/graphql/gql.ts
6665
6693
  var debug7 = Debug6("mobbdev:gql");
6694
+ function isAuthError(error) {
6695
+ if (error instanceof ClientError) {
6696
+ const gqlErrors = error.response?.errors;
6697
+ return gqlErrors?.some(
6698
+ (e) => e.extensions?.["code"] === "access-denied" || e.message?.includes("Authentication hook unauthorized")
6699
+ ) ?? false;
6700
+ }
6701
+ return false;
6702
+ }
6703
+ function isNetworkError(error) {
6704
+ const errorString = error?.toString() ?? "";
6705
+ return errorString.includes("FetchError") || errorString.includes("TypeError") || errorString.includes("ECONNREFUSED") || errorString.includes("ENOTFOUND") || errorString.includes("ETIMEDOUT") || errorString.includes("UND_ERR");
6706
+ }
6666
6707
  var API_KEY_HEADER_NAME = "x-mobb-key";
6667
6708
  var REPORT_STATE_CHECK_DELAY = 5 * 1e3;
6668
6709
  var GQLClient = class {
@@ -6722,23 +6763,35 @@ var GQLClient = class {
6722
6763
  try {
6723
6764
  await this.getUserInfo();
6724
6765
  } catch (e) {
6725
- if (e?.toString().startsWith("FetchError")) {
6726
- debug7("verify connection failed %o", e);
6766
+ if (isNetworkError(e)) {
6767
+ debug7("verify connection failed (network error) %o", e);
6727
6768
  return false;
6728
6769
  }
6770
+ debug7("verify connection: endpoint reachable but request failed %o", e);
6729
6771
  }
6730
6772
  return true;
6731
6773
  }
6732
6774
  async validateUserToken() {
6733
- await this.createCommunityUser();
6734
- let info;
6735
6775
  try {
6736
- info = await this.getUserInfo();
6776
+ await this.createCommunityUser();
6777
+ const info = await this.getUserInfo();
6778
+ if (!info) {
6779
+ debug7("verify token failed - no user info returned");
6780
+ return false;
6781
+ }
6782
+ return info.email || true;
6737
6783
  } catch (e) {
6738
- debug7("verify token failed %o", e);
6739
- return false;
6784
+ if (isAuthError(e)) {
6785
+ debug7("verify token failed - auth error %o", e);
6786
+ return false;
6787
+ }
6788
+ if (isNetworkError(e)) {
6789
+ debug7("verify token failed - network error, rethrowing %o", e);
6790
+ throw e;
6791
+ }
6792
+ debug7("verify token failed - unexpected error, rethrowing %o", e);
6793
+ throw e;
6740
6794
  }
6741
- return info?.email || true;
6742
6795
  }
6743
6796
  async getLastOrgAndNamedProject(params) {
6744
6797
  const me = await this.getUserInfo();
@@ -7094,6 +7147,12 @@ var GQLClient = class {
7094
7147
  async finalizeAIBlameInferencesUploadRaw(variables) {
7095
7148
  return await this._clientSdk.FinalizeAIBlameInferencesUpload(variables);
7096
7149
  }
7150
+ async uploadTracyRecords(variables) {
7151
+ return await this._clientSdk.UploadTracyRecords(variables);
7152
+ }
7153
+ async getTracyRawDataUploadUrls(variables) {
7154
+ return await this._clientSdk.GetTracyRawDataUploadUrls(variables);
7155
+ }
7097
7156
  async analyzeCommitForExtensionAIBlame(variables) {
7098
7157
  return await this._clientSdk.AnalyzeCommitForExtensionAIBlame(variables);
7099
7158
  }
@@ -7111,6 +7170,209 @@ var GQLClient = class {
7111
7170
  }
7112
7171
  };
7113
7172
 
7173
+ // src/features/analysis/graphql/tracy-batch-upload.ts
7174
+ import { promisify } from "util";
7175
+ import { gzip } from "zlib";
7176
+ import Debug8 from "debug";
7177
+
7178
+ // src/utils/sanitize-sensitive-data.ts
7179
+ import { OpenRedaction } from "@openredaction/openredaction";
7180
+ var openRedaction = new OpenRedaction({
7181
+ patterns: [
7182
+ // Core Personal Data
7183
+ // Removed EMAIL - causes false positives in code/test snippets (e.g. --author="Eve Author <eve@example.com>")
7184
+ // Prefer false negatives over false positives for this use case.
7185
+ "SSN",
7186
+ "NATIONAL_INSURANCE_UK",
7187
+ "DATE_OF_BIRTH",
7188
+ // Identity Documents
7189
+ "PASSPORT_UK",
7190
+ "PASSPORT_US",
7191
+ "PASSPORT_MRZ_TD1",
7192
+ "PASSPORT_MRZ_TD3",
7193
+ "DRIVING_LICENSE_UK",
7194
+ "DRIVING_LICENSE_US",
7195
+ "VISA_NUMBER",
7196
+ "VISA_MRZ",
7197
+ "TAX_ID",
7198
+ // Financial Data (removed SWIFT_BIC, CARD_AUTH_CODE - too broad, causing false positives with authentication words)
7199
+ // Removed CREDIT_CARD - causes false positives on zero-filled UUIDs (e.g. '00000000-0000-0000-0000-000000000000')
7200
+ // Prefer false negatives over false positives for this use case.
7201
+ "IBAN",
7202
+ "BANK_ACCOUNT_UK",
7203
+ "ROUTING_NUMBER_US",
7204
+ "CARD_TRACK1_DATA",
7205
+ "CARD_TRACK2_DATA",
7206
+ "CARD_EXPIRY",
7207
+ // Cryptocurrency (removed BITCOIN_ADDRESS - too broad, matches hash-like strings)
7208
+ "ETHEREUM_ADDRESS",
7209
+ "LITECOIN_ADDRESS",
7210
+ "CARDANO_ADDRESS",
7211
+ "SOLANA_ADDRESS",
7212
+ "MONERO_ADDRESS",
7213
+ "RIPPLE_ADDRESS",
7214
+ // Medical Data (removed PRESCRIPTION_NUMBER - too broad, matches words containing "ription")
7215
+ // Removed MEDICAL_RECORD_NUMBER - too broad, "MR" prefix matches "Merge Request" in SCM contexts (e.g. "MR branches" → "MR br****es")
7216
+ "NHS_NUMBER",
7217
+ "AUSTRALIAN_MEDICARE",
7218
+ "HEALTH_PLAN_NUMBER",
7219
+ "PATIENT_ID",
7220
+ // Communications (removed EMERGENCY_CONTACT, ADDRESS_PO_BOX, ZIP_CODE_US, PHONE_US, PHONE_INTERNATIONAL - too broad, causing false positives)
7221
+ "PHONE_UK",
7222
+ "PHONE_UK_MOBILE",
7223
+ "PHONE_LINE_NUMBER",
7224
+ "ADDRESS_STREET",
7225
+ "POSTCODE_UK",
7226
+ // Network & Technical
7227
+ "IPV4",
7228
+ "IPV6",
7229
+ "MAC_ADDRESS",
7230
+ "URL_WITH_AUTH",
7231
+ // Security Keys & Tokens
7232
+ "PRIVATE_KEY",
7233
+ "SSH_PRIVATE_KEY",
7234
+ "AWS_SECRET_KEY",
7235
+ "AWS_ACCESS_KEY",
7236
+ "AZURE_STORAGE_KEY",
7237
+ "GCP_SERVICE_ACCOUNT",
7238
+ "JWT_TOKEN",
7239
+ "OAUTH_TOKEN",
7240
+ "OAUTH_CLIENT_SECRET",
7241
+ "BEARER_TOKEN",
7242
+ "PAYMENT_TOKEN",
7243
+ "GENERIC_SECRET",
7244
+ "GENERIC_API_KEY",
7245
+ // Platform-Specific API Keys
7246
+ "GITHUB_TOKEN",
7247
+ "SLACK_TOKEN",
7248
+ "STRIPE_API_KEY",
7249
+ "GOOGLE_API_KEY",
7250
+ "FIREBASE_API_KEY",
7251
+ "HEROKU_API_KEY",
7252
+ "MAILGUN_API_KEY",
7253
+ "SENDGRID_API_KEY",
7254
+ "TWILIO_API_KEY",
7255
+ "NPM_TOKEN",
7256
+ "PYPI_TOKEN",
7257
+ "DOCKER_AUTH",
7258
+ "KUBERNETES_SECRET",
7259
+ // Government & Legal
7260
+ // Removed CLIENT_ID - too broad, "client" is ubiquitous in code (npm packages like @scope/client-*, class names like ClientSdkOptions)
7261
+ "POLICE_REPORT_NUMBER",
7262
+ "IMMIGRATION_NUMBER",
7263
+ "COURT_REPORTER_LICENSE"
7264
+ ]
7265
+ });
7266
+ function maskString(str, showStart = 2, showEnd = 2) {
7267
+ if (str.length <= showStart + showEnd) {
7268
+ return "*".repeat(str.length);
7269
+ }
7270
+ return str.slice(0, showStart) + "*".repeat(str.length - showStart - showEnd) + str.slice(-showEnd);
7271
+ }
7272
+ async function sanitizeDataWithCounts(obj) {
7273
+ const counts = {
7274
+ detections: { total: 0, high: 0, medium: 0, low: 0 }
7275
+ };
7276
+ const sanitizeString = async (str) => {
7277
+ let result = str;
7278
+ const piiDetections = openRedaction.scan(str);
7279
+ if (piiDetections && piiDetections.total > 0) {
7280
+ const allDetections = [
7281
+ ...piiDetections.high,
7282
+ ...piiDetections.medium,
7283
+ ...piiDetections.low
7284
+ ];
7285
+ for (const detection of allDetections) {
7286
+ counts.detections.total++;
7287
+ if (detection.severity === "high") counts.detections.high++;
7288
+ else if (detection.severity === "medium") counts.detections.medium++;
7289
+ else if (detection.severity === "low") counts.detections.low++;
7290
+ const masked = maskString(detection.value);
7291
+ result = result.replaceAll(detection.value, masked);
7292
+ }
7293
+ }
7294
+ return result;
7295
+ };
7296
+ const sanitizeRecursive = async (data) => {
7297
+ if (typeof data === "string") {
7298
+ return sanitizeString(data);
7299
+ } else if (Array.isArray(data)) {
7300
+ return Promise.all(data.map((item) => sanitizeRecursive(item)));
7301
+ } else if (data instanceof Error) {
7302
+ return data;
7303
+ } else if (data instanceof Date) {
7304
+ return data;
7305
+ } else if (typeof data === "object" && data !== null) {
7306
+ const sanitized = {};
7307
+ const record = data;
7308
+ for (const key in record) {
7309
+ if (Object.prototype.hasOwnProperty.call(record, key)) {
7310
+ sanitized[key] = await sanitizeRecursive(record[key]);
7311
+ }
7312
+ }
7313
+ return sanitized;
7314
+ }
7315
+ return data;
7316
+ };
7317
+ const sanitizedData = await sanitizeRecursive(obj);
7318
+ return { sanitizedData, counts };
7319
+ }
7320
+
7321
+ // src/features/analysis/upload-file.ts
7322
+ import Debug7 from "debug";
7323
+ import fetch3, { File, fileFrom, FormData } from "node-fetch";
7324
+ var debug8 = Debug7("mobbdev:upload-file");
7325
+ async function uploadFile({
7326
+ file,
7327
+ url,
7328
+ uploadKey,
7329
+ uploadFields,
7330
+ logger
7331
+ }) {
7332
+ const logInfo = logger || ((_message, _data) => {
7333
+ });
7334
+ logInfo(`FileUpload: upload file start ${url}`);
7335
+ logInfo(`FileUpload: upload fields`, uploadFields);
7336
+ logInfo(`FileUpload: upload key ${uploadKey}`);
7337
+ debug8("upload file start %s", url);
7338
+ debug8("upload fields %o", uploadFields);
7339
+ debug8("upload key %s", uploadKey);
7340
+ const form = new FormData();
7341
+ Object.entries(uploadFields).forEach(([key, value]) => {
7342
+ form.append(key, value);
7343
+ });
7344
+ if (!form.has("key")) {
7345
+ form.append("key", uploadKey);
7346
+ }
7347
+ if (typeof file === "string") {
7348
+ debug8("upload file from path %s", file);
7349
+ logInfo(`FileUpload: upload file from path ${file}`);
7350
+ form.append("file", await fileFrom(file));
7351
+ } else {
7352
+ debug8("upload file from buffer");
7353
+ logInfo(`FileUpload: upload file from buffer`);
7354
+ form.append("file", new File([new Uint8Array(file)], "file"));
7355
+ }
7356
+ const agent = getProxyAgent(url);
7357
+ const response = await fetch3(url, {
7358
+ method: "POST",
7359
+ body: form,
7360
+ agent
7361
+ });
7362
+ if (!response.ok) {
7363
+ debug8("error from S3 %s %s", response.body, response.status);
7364
+ logInfo(`FileUpload: error from S3 ${response.body} ${response.status}`);
7365
+ throw new Error(`Failed to upload the file: ${response.status}`);
7366
+ }
7367
+ debug8("upload file done");
7368
+ logInfo(`FileUpload: upload file done`);
7369
+ }
7370
+
7371
+ // src/features/analysis/graphql/tracy-batch-upload.ts
7372
+ var gzipAsync = promisify(gzip);
7373
+ var debug9 = Debug8("mobbdev:tracy-batch-upload");
7374
+ var MAX_BATCH_PAYLOAD_BYTES = 3 * 1024 * 1024;
7375
+
7114
7376
  // src/mcp/services/types.ts
7115
7377
  function buildLoginUrl(baseUrl, loginId, hostname, context) {
7116
7378
  const url = new URL(`${baseUrl}/${loginId}`);
@@ -7143,9 +7405,10 @@ function getConfigStore() {
7143
7405
  var configStore = getConfigStore();
7144
7406
 
7145
7407
  // src/commands/AuthManager.ts
7408
+ var debug10 = Debug9("mobbdev:auth");
7146
7409
  var LOGIN_MAX_WAIT = 10 * 60 * 1e3;
7147
7410
  var LOGIN_CHECK_DELAY = 5 * 1e3;
7148
- var AuthManager = class {
7411
+ var _AuthManager = class _AuthManager {
7149
7412
  constructor(webAppUrl, apiUrl) {
7150
7413
  __publicField(this, "publicKey");
7151
7414
  __publicField(this, "privateKey");
@@ -7167,7 +7430,7 @@ var AuthManager = class {
7167
7430
  }
7168
7431
  async waitForAuthentication() {
7169
7432
  let newApiToken = null;
7170
- for (let i = 0; i < LOGIN_MAX_WAIT / LOGIN_CHECK_DELAY; i++) {
7433
+ for (let i = 0; i < _AuthManager.loginMaxWait / LOGIN_CHECK_DELAY; i++) {
7171
7434
  newApiToken = await this.getApiToken();
7172
7435
  if (newApiToken) {
7173
7436
  break;
@@ -7197,6 +7460,9 @@ var AuthManager = class {
7197
7460
  if (this.authenticated === null) {
7198
7461
  const result = await this.checkAuthentication();
7199
7462
  this.authenticated = result.isAuthenticated;
7463
+ if (!result.isAuthenticated) {
7464
+ debug10("isAuthenticated: false \u2014 %s", result.message);
7465
+ }
7200
7466
  }
7201
7467
  return this.authenticated;
7202
7468
  }
@@ -7273,6 +7539,14 @@ var AuthManager = class {
7273
7539
  }
7274
7540
  return null;
7275
7541
  }
7542
+ /**
7543
+ * Returns true if a non-empty API token is stored in the configStore.
7544
+ * Used for diagnostics — does NOT validate the token.
7545
+ */
7546
+ hasStoredToken() {
7547
+ const token = configStore.get("apiToken");
7548
+ return typeof token === "string" && token.length > 0;
7549
+ }
7276
7550
  /**
7277
7551
  * Gets the current GQL client (if authenticated)
7278
7552
  */
@@ -7305,9 +7579,12 @@ var AuthManager = class {
7305
7579
  this.currentBrowserUrl = null;
7306
7580
  }
7307
7581
  };
7582
+ /** Maximum time (ms) to wait for login authentication. Override in tests for faster failures. */
7583
+ __publicField(_AuthManager, "loginMaxWait", LOGIN_MAX_WAIT);
7584
+ var AuthManager = _AuthManager;
7308
7585
 
7309
7586
  // src/commands/handleMobbLogin.ts
7310
- var debug8 = Debug7("mobbdev:commands");
7587
+ var debug11 = Debug10("mobbdev:commands");
7311
7588
  var LOGIN_MAX_WAIT2 = 10 * 60 * 1e3;
7312
7589
  var LOGIN_CHECK_DELAY2 = 5 * 1e3;
7313
7590
  var MOBB_LOGIN_REQUIRED_MSG = `\u{1F513} Login to Mobb is Required, you will be redirected to our login page, once the authorization is complete return to this prompt, ${chalk2.bgBlue(
@@ -7319,7 +7596,7 @@ async function getAuthenticatedGQLClient({
7319
7596
  apiUrl,
7320
7597
  webAppUrl
7321
7598
  }) {
7322
- debug8(
7599
+ debug11(
7323
7600
  "getAuthenticatedGQLClient called with: apiUrl=%s, webAppUrl=%s",
7324
7601
  apiUrl || "undefined",
7325
7602
  webAppUrl || "undefined"
@@ -7342,7 +7619,7 @@ async function handleMobbLogin({
7342
7619
  webAppUrl,
7343
7620
  loginContext
7344
7621
  }) {
7345
- debug8(
7622
+ debug11(
7346
7623
  "handleMobbLogin: resolved URLs - apiUrl=%s (from param: %s), webAppUrl=%s (from param: %s)",
7347
7624
  apiUrl || "fallback",
7348
7625
  apiUrl || "fallback",
@@ -7361,7 +7638,7 @@ async function handleMobbLogin({
7361
7638
  return authManager.getGQLClient();
7362
7639
  }
7363
7640
  } catch (error) {
7364
- debug8("Authentication check failed:", error);
7641
+ debug11("Authentication check failed:", error);
7365
7642
  }
7366
7643
  if (apiKey) {
7367
7644
  createSpinner().start().error({
@@ -7412,56 +7689,6 @@ init_client_generates();
7412
7689
  init_GitService();
7413
7690
  init_urlParser2();
7414
7691
 
7415
- // src/features/analysis/upload-file.ts
7416
- import Debug8 from "debug";
7417
- import fetch3, { File, fileFrom, FormData } from "node-fetch";
7418
- var debug9 = Debug8("mobbdev:upload-file");
7419
- async function uploadFile({
7420
- file,
7421
- url,
7422
- uploadKey,
7423
- uploadFields,
7424
- logger
7425
- }) {
7426
- const logInfo = logger || ((_message, _data) => {
7427
- });
7428
- logInfo(`FileUpload: upload file start ${url}`);
7429
- logInfo(`FileUpload: upload fields`, uploadFields);
7430
- logInfo(`FileUpload: upload key ${uploadKey}`);
7431
- debug9("upload file start %s", url);
7432
- debug9("upload fields %o", uploadFields);
7433
- debug9("upload key %s", uploadKey);
7434
- const form = new FormData();
7435
- Object.entries(uploadFields).forEach(([key, value]) => {
7436
- form.append(key, value);
7437
- });
7438
- if (!form.has("key")) {
7439
- form.append("key", uploadKey);
7440
- }
7441
- if (typeof file === "string") {
7442
- debug9("upload file from path %s", file);
7443
- logInfo(`FileUpload: upload file from path ${file}`);
7444
- form.append("file", await fileFrom(file));
7445
- } else {
7446
- debug9("upload file from buffer");
7447
- logInfo(`FileUpload: upload file from buffer`);
7448
- form.append("file", new File([new Uint8Array(file)], "file"));
7449
- }
7450
- const agent = getProxyAgent(url);
7451
- const response = await fetch3(url, {
7452
- method: "POST",
7453
- body: form,
7454
- agent
7455
- });
7456
- if (!response.ok) {
7457
- debug9("error from S3 %s %s", response.body, response.status);
7458
- logInfo(`FileUpload: error from S3 ${response.body} ${response.status}`);
7459
- throw new Error(`Failed to upload the file: ${response.status}`);
7460
- }
7461
- debug9("upload file done");
7462
- logInfo(`FileUpload: upload file done`);
7463
- }
7464
-
7465
7692
  // src/utils/computerName.ts
7466
7693
  import { execSync } from "child_process";
7467
7694
  import os2 from "os";
@@ -7551,149 +7778,6 @@ function getStableComputerName() {
7551
7778
  return currentName;
7552
7779
  }
7553
7780
 
7554
- // src/utils/sanitize-sensitive-data.ts
7555
- import { OpenRedaction } from "@openredaction/openredaction";
7556
- var openRedaction = new OpenRedaction({
7557
- patterns: [
7558
- // Core Personal Data
7559
- // Removed EMAIL - causes false positives in code/test snippets (e.g. --author="Eve Author <eve@example.com>")
7560
- // Prefer false negatives over false positives for this use case.
7561
- "SSN",
7562
- "NATIONAL_INSURANCE_UK",
7563
- "DATE_OF_BIRTH",
7564
- // Identity Documents
7565
- "PASSPORT_UK",
7566
- "PASSPORT_US",
7567
- "PASSPORT_MRZ_TD1",
7568
- "PASSPORT_MRZ_TD3",
7569
- "DRIVING_LICENSE_UK",
7570
- "DRIVING_LICENSE_US",
7571
- "VISA_NUMBER",
7572
- "VISA_MRZ",
7573
- "TAX_ID",
7574
- // Financial Data (removed SWIFT_BIC, CARD_AUTH_CODE - too broad, causing false positives with authentication words)
7575
- // Removed CREDIT_CARD - causes false positives on zero-filled UUIDs (e.g. '00000000-0000-0000-0000-000000000000')
7576
- // Prefer false negatives over false positives for this use case.
7577
- "IBAN",
7578
- "BANK_ACCOUNT_UK",
7579
- "ROUTING_NUMBER_US",
7580
- "CARD_TRACK1_DATA",
7581
- "CARD_TRACK2_DATA",
7582
- "CARD_EXPIRY",
7583
- // Cryptocurrency (removed BITCOIN_ADDRESS - too broad, matches hash-like strings)
7584
- "ETHEREUM_ADDRESS",
7585
- "LITECOIN_ADDRESS",
7586
- "CARDANO_ADDRESS",
7587
- "SOLANA_ADDRESS",
7588
- "MONERO_ADDRESS",
7589
- "RIPPLE_ADDRESS",
7590
- // Medical Data (removed PRESCRIPTION_NUMBER - too broad, matches words containing "ription")
7591
- // Removed MEDICAL_RECORD_NUMBER - too broad, "MR" prefix matches "Merge Request" in SCM contexts (e.g. "MR branches" → "MR br****es")
7592
- "NHS_NUMBER",
7593
- "AUSTRALIAN_MEDICARE",
7594
- "HEALTH_PLAN_NUMBER",
7595
- "PATIENT_ID",
7596
- // Communications (removed EMERGENCY_CONTACT, ADDRESS_PO_BOX, ZIP_CODE_US, PHONE_US, PHONE_INTERNATIONAL - too broad, causing false positives)
7597
- "PHONE_UK",
7598
- "PHONE_UK_MOBILE",
7599
- "PHONE_LINE_NUMBER",
7600
- "ADDRESS_STREET",
7601
- "POSTCODE_UK",
7602
- // Network & Technical
7603
- "IPV4",
7604
- "IPV6",
7605
- "MAC_ADDRESS",
7606
- "URL_WITH_AUTH",
7607
- // Security Keys & Tokens
7608
- "PRIVATE_KEY",
7609
- "SSH_PRIVATE_KEY",
7610
- "AWS_SECRET_KEY",
7611
- "AWS_ACCESS_KEY",
7612
- "AZURE_STORAGE_KEY",
7613
- "GCP_SERVICE_ACCOUNT",
7614
- "JWT_TOKEN",
7615
- "OAUTH_TOKEN",
7616
- "OAUTH_CLIENT_SECRET",
7617
- "BEARER_TOKEN",
7618
- "PAYMENT_TOKEN",
7619
- "GENERIC_SECRET",
7620
- "GENERIC_API_KEY",
7621
- // Platform-Specific API Keys
7622
- "GITHUB_TOKEN",
7623
- "SLACK_TOKEN",
7624
- "STRIPE_API_KEY",
7625
- "GOOGLE_API_KEY",
7626
- "FIREBASE_API_KEY",
7627
- "HEROKU_API_KEY",
7628
- "MAILGUN_API_KEY",
7629
- "SENDGRID_API_KEY",
7630
- "TWILIO_API_KEY",
7631
- "NPM_TOKEN",
7632
- "PYPI_TOKEN",
7633
- "DOCKER_AUTH",
7634
- "KUBERNETES_SECRET",
7635
- // Government & Legal
7636
- // Removed CLIENT_ID - too broad, "client" is ubiquitous in code (npm packages like @scope/client-*, class names like ClientSdkOptions)
7637
- "POLICE_REPORT_NUMBER",
7638
- "IMMIGRATION_NUMBER",
7639
- "COURT_REPORTER_LICENSE"
7640
- ]
7641
- });
7642
- function maskString(str, showStart = 2, showEnd = 2) {
7643
- if (str.length <= showStart + showEnd) {
7644
- return "*".repeat(str.length);
7645
- }
7646
- return str.slice(0, showStart) + "*".repeat(str.length - showStart - showEnd) + str.slice(-showEnd);
7647
- }
7648
- async function sanitizeDataWithCounts(obj) {
7649
- const counts = {
7650
- detections: { total: 0, high: 0, medium: 0, low: 0 }
7651
- };
7652
- const sanitizeString = async (str) => {
7653
- let result = str;
7654
- const piiDetections = openRedaction.scan(str);
7655
- if (piiDetections && piiDetections.total > 0) {
7656
- const allDetections = [
7657
- ...piiDetections.high,
7658
- ...piiDetections.medium,
7659
- ...piiDetections.low
7660
- ];
7661
- for (const detection of allDetections) {
7662
- counts.detections.total++;
7663
- if (detection.severity === "high") counts.detections.high++;
7664
- else if (detection.severity === "medium") counts.detections.medium++;
7665
- else if (detection.severity === "low") counts.detections.low++;
7666
- const masked = maskString(detection.value);
7667
- result = result.replaceAll(detection.value, masked);
7668
- }
7669
- }
7670
- return result;
7671
- };
7672
- const sanitizeRecursive = async (data) => {
7673
- if (typeof data === "string") {
7674
- return sanitizeString(data);
7675
- } else if (Array.isArray(data)) {
7676
- return Promise.all(data.map((item) => sanitizeRecursive(item)));
7677
- } else if (data instanceof Error) {
7678
- return data;
7679
- } else if (data instanceof Date) {
7680
- return data;
7681
- } else if (typeof data === "object" && data !== null) {
7682
- const sanitized = {};
7683
- const record = data;
7684
- for (const key in record) {
7685
- if (Object.prototype.hasOwnProperty.call(record, key)) {
7686
- sanitized[key] = await sanitizeRecursive(record[key]);
7687
- }
7688
- }
7689
- return sanitized;
7690
- }
7691
- return data;
7692
- };
7693
- const sanitizedData = await sanitizeRecursive(obj);
7694
- return { sanitizedData, counts };
7695
- }
7696
-
7697
7781
  // src/args/commands/upload_ai_blame.ts
7698
7782
  var defaultLogger = {
7699
7783
  info: (msg, data) => {
@@ -8014,6 +8098,8 @@ async function uploadAiBlameCommandHandler(args) {
8014
8098
  await uploadAiBlameHandler({ args });
8015
8099
  }
8016
8100
  export {
8101
+ getRepositoryUrl,
8102
+ getSystemInfo,
8017
8103
  uploadAiBlameBuilder,
8018
8104
  uploadAiBlameCommandHandler,
8019
8105
  uploadAiBlameHandler,