@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/README.md +10 -5
- package/dist/action.js +98 -34
- package/dist/action.js.map +1 -1
- package/dist/cli.js +103 -39
- package/dist/cli.js.map +1 -1
- package/package.json +4 -2
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
|
-
|
|
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
|
-
|
|
9331
|
-
|
|
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
|
|
9448
|
-
|
|
9449
|
-
|
|
9450
|
-
|
|
9451
|
-
|
|
9452
|
-
|
|
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
|
-
|
|
9457
|
-
|
|
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
|
|
9473
|
-
auth.cookies =
|
|
9536
|
+
if (hasCookies) {
|
|
9537
|
+
auth.cookies = cookies;
|
|
9474
9538
|
}
|
|
9475
|
-
if (hasHeaders && options
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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(' --
|
|
9574
|
-
console.log('
|
|
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(' #
|
|
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
|