@xcelera/cli 2.3.0 → 2.4.0

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/cli.js CHANGED
@@ -9276,7 +9276,7 @@ var simpleGit = gitInstanceFactory;
9276
9276
 
9277
9277
  async function inferGitContext() {
9278
9278
  if (!(await isGitRepository())) {
9279
- throw new Error('No git repository detected.');
9279
+ return undefined;
9280
9280
  }
9281
9281
  const remoteUrl = await getRemoteUrl();
9282
9282
  const parsed = parseGithubUrl(remoteUrl);
@@ -9326,27 +9326,100 @@ async function getCommit(hash = 'HEAD') {
9326
9326
 
9327
9327
  async function inferBuildContext() {
9328
9328
  const ciEnv = envCi();
9329
- const gitContext = {
9330
- ...(await inferGitContext()),
9331
- branch: ('prBranch' in ciEnv && ciEnv.prBranch ? ciEnv.prBranch : ciEnv.branch) ??
9332
- undefined
9333
- };
9329
+ const gitContext = await inferGitContext();
9330
+ const branch = ('prBranch' in ciEnv && ciEnv.prBranch ? ciEnv.prBranch : ciEnv.branch) ??
9331
+ undefined;
9334
9332
  return {
9335
9333
  service: ciEnv.isCi ? ciEnv.service : 'unknown',
9336
9334
  prNumber: 'pr' in ciEnv ? ciEnv.pr : undefined,
9337
9335
  buildNumber: 'build' in ciEnv ? ciEnv.build : undefined,
9338
9336
  buildUrl: 'buildUrl' in ciEnv ? ciEnv.buildUrl : undefined,
9339
- git: gitContext
9337
+ git: gitContext ? { ...gitContext, branch } : undefined
9340
9338
  };
9341
9339
  }
9342
9340
 
9341
+ function readNetscapeCookieFileSync(filePath, now = new Date()) {
9342
+ try {
9343
+ const contents = readFileSync(filePath, 'utf8');
9344
+ return parseNetscapeCookieFileContents(contents, filePath, now);
9345
+ }
9346
+ catch (error) {
9347
+ const message = error instanceof Error ? error.message : String(error);
9348
+ throw new Error(`Unable to read cookie file "${filePath}": ${message}`);
9349
+ }
9350
+ }
9351
+ function parseNetscapeCookieFileContents(contents, sourceLabel = 'cookie file', now = new Date()) {
9352
+ const cookies = [];
9353
+ const expiredCookieNames = [];
9354
+ const nowEpochSeconds = Math.floor(now.getTime() / 1000);
9355
+ const lines = contents.split(/\r?\n/);
9356
+ for (let index = 0; index < lines.length; index++) {
9357
+ const rawLine = lines[index];
9358
+ const line = rawLine.trim();
9359
+ if (!line) {
9360
+ continue;
9361
+ }
9362
+ if (line.startsWith('#') && !line.startsWith('#HttpOnly_')) {
9363
+ continue;
9364
+ }
9365
+ const fields = line.split('\t');
9366
+ if (fields.length !== 7) {
9367
+ throw new Error(`Invalid Netscape cookie line ${index + 1} in ${sourceLabel}: expected 7 tab-separated fields`);
9368
+ }
9369
+ let domain = fields[0];
9370
+ const path = fields[2] || undefined;
9371
+ const secure = toBool(fields[3]);
9372
+ const expiresEpochSeconds = parseEpochSeconds(fields[4], index + 1, sourceLabel);
9373
+ const name = fields[5];
9374
+ const value = fields[6] ?? '';
9375
+ let httpOnly;
9376
+ if (domain.startsWith('#HttpOnly_')) {
9377
+ httpOnly = true;
9378
+ domain = domain.slice('#HttpOnly_'.length);
9379
+ }
9380
+ const isExpired = expiresEpochSeconds > 0 && expiresEpochSeconds < nowEpochSeconds;
9381
+ if (isExpired) {
9382
+ expiredCookieNames.push(name);
9383
+ continue;
9384
+ }
9385
+ const cookie = {
9386
+ name,
9387
+ value,
9388
+ ...(domain && { domain }),
9389
+ ...(path && { path }),
9390
+ ...(secure && { secure: true }),
9391
+ ...(httpOnly && { httpOnly: true })
9392
+ };
9393
+ cookies.push(cookie);
9394
+ }
9395
+ const warnings = [];
9396
+ if (expiredCookieNames.length > 0) {
9397
+ const preview = expiredCookieNames.slice(0, 5).join(', ');
9398
+ const suffix = expiredCookieNames.length > 5 ? ', …' : '';
9399
+ warnings.push(`⚠️ Dropped ${expiredCookieNames.length} expired cookie(s) from ${sourceLabel}: ${preview}${suffix}`);
9400
+ }
9401
+ return { cookies, warnings };
9402
+ }
9403
+ function toBool(value) {
9404
+ return value.trim().toUpperCase() === 'TRUE';
9405
+ }
9406
+ function parseEpochSeconds(value, lineNumber, sourceLabel) {
9407
+ const trimmed = value.trim();
9408
+ const num = Number.parseInt(trimmed, 10);
9409
+ if (Number.isNaN(num)) {
9410
+ throw new Error(`Invalid expiration epoch seconds at line ${lineNumber} in ${sourceLabel}`);
9411
+ }
9412
+ return num;
9413
+ }
9414
+
9343
9415
  async function runAuditCommand(url, token, authOptions) {
9344
9416
  const output = [];
9345
9417
  const errors = [];
9346
9418
  try {
9347
9419
  const buildContext = await inferBuildContext();
9348
9420
  output.push(...formatBuildContext(buildContext));
9349
- const auth = parseAuthCredentials(authOptions);
9421
+ const { auth, warnings } = parseAuthCredentials(authOptions);
9422
+ errors.push(...warnings);
9350
9423
  if (auth) {
9351
9424
  output.push('🔐 Authentication credentials detected');
9352
9425
  output.push('');
@@ -9444,35 +9517,26 @@ function formatGitHubIntegrationStatus(context) {
9444
9517
  return { output, errors };
9445
9518
  }
9446
9519
  function parseAuthCredentials(options) {
9447
- const envAuth = process.env.XCELERA_AUTH;
9448
- if (envAuth) {
9449
- try {
9450
- return JSON.parse(envAuth);
9451
- }
9452
- catch {
9453
- throw new Error('XCELERA_AUTH environment variable contains invalid JSON');
9454
- }
9520
+ const warnings = [];
9521
+ const cookies = [];
9522
+ if (options?.cookieFile) {
9523
+ const parsed = readNetscapeCookieFileSync(options.cookieFile);
9524
+ warnings.push(...parsed.warnings);
9525
+ cookies.push(...parsed.cookies);
9455
9526
  }
9456
- // Check for --auth JSON option
9457
- if (options?.authJson) {
9458
- try {
9459
- return JSON.parse(options.authJson);
9460
- }
9461
- catch {
9462
- throw new Error('--auth option contains invalid JSON');
9463
- }
9527
+ if (options?.cookies && options.cookies.length > 0) {
9528
+ cookies.push(...options.cookies.map(parseCookie));
9464
9529
  }
9465
- // Build auth from --cookie and --header options
9466
- const hasCookies = options?.cookies && options.cookies.length > 0;
9467
9530
  const hasHeaders = options?.headers && options.headers.length > 0;
9531
+ const hasCookies = cookies.length > 0;
9468
9532
  if (!hasCookies && !hasHeaders) {
9469
- return undefined;
9533
+ return { auth: undefined, warnings };
9470
9534
  }
9471
9535
  const auth = {};
9472
- if (hasCookies && options.cookies) {
9473
- auth.cookies = options.cookies.map(parseCookie);
9536
+ if (hasCookies) {
9537
+ auth.cookies = cookies;
9474
9538
  }
9475
- if (hasHeaders && options.headers) {
9539
+ if (hasHeaders && options?.headers) {
9476
9540
  auth.headers = {};
9477
9541
  for (const header of options.headers) {
9478
9542
  const colonIndex = header.indexOf(':');
@@ -9484,7 +9548,7 @@ function parseAuthCredentials(options) {
9484
9548
  auth.headers[name] = value;
9485
9549
  }
9486
9550
  }
9487
- return auth;
9551
+ return { auth, warnings };
9488
9552
  }
9489
9553
  function parseCookie(cookie) {
9490
9554
  const equalsIndex = cookie.indexOf('=');
@@ -9508,7 +9572,7 @@ const options = {
9508
9572
  required: true,
9509
9573
  default: process.env.XCELERA_TOKEN
9510
9574
  },
9511
- auth: {
9575
+ 'cookie-file': {
9512
9576
  type: 'string'
9513
9577
  },
9514
9578
  cookie: {
@@ -9540,7 +9604,8 @@ if (command !== 'audit') {
9540
9604
  printHelp();
9541
9605
  process.exit(1);
9542
9606
  }
9543
- const { url, token, auth, cookie, header } = values;
9607
+ const { url, token, cookie, header } = values;
9608
+ const cookieFile = values['cookie-file'];
9544
9609
  if (!url) {
9545
9610
  console.error('URL is required. Use --url <url> to specify the URL to audit.');
9546
9611
  process.exit(1);
@@ -9550,7 +9615,7 @@ if (!token) {
9550
9615
  process.exit(1);
9551
9616
  }
9552
9617
  const result = await runAuditCommand(url, token, {
9553
- authJson: auth,
9618
+ cookieFile,
9554
9619
  cookies: cookie,
9555
9620
  headers: header
9556
9621
  });
@@ -9570,8 +9635,8 @@ function printHelp() {
9570
9635
  console.log(' Can be specified multiple times.');
9571
9636
  console.log(' --header <header> Header in "Name: Value" format.');
9572
9637
  console.log(' Can be specified multiple times.');
9573
- console.log(' --auth <json> Full auth config as JSON.');
9574
- console.log(' Can also be set with XCELERA_AUTH env var.');
9638
+ console.log(' --cookie-file <path> Netscape cookie file (cookies.txt).');
9639
+ console.log(' Expired cookies are ignored with a warning.');
9575
9640
  console.log('');
9576
9641
  console.log('Examples:');
9577
9642
  console.log(' # Basic audit');
@@ -9588,9 +9653,8 @@ function printHelp() {
9588
9653
  console.log(' xcelera audit --url https://myapp.com/dashboard \\');
9589
9654
  console.log(' --cookie "session=abc123" --cookie "csrf=xyz"');
9590
9655
  console.log('');
9591
- console.log(' # Full auth JSON (multiple cookies and headers)');
9592
- console.log(' xcelera audit --url https://myapp.com/dashboard \\');
9593
- console.log(' --auth \'{"cookies":[{"name":"session","value":"abc123"}],"headers":{"X-Custom":"value"}}\'');
9656
+ console.log(' # With cookie file (Netscape cookies.txt format)');
9657
+ console.log(' xcelera audit --url https://myapp.com/dashboard --cookie-file ./cookies.txt');
9594
9658
  console.log('');
9595
9659
  }
9596
9660
  //# sourceMappingURL=cli.js.map