New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

@box/cli

Package Overview
Dependencies
Maintainers
2
Versions
45
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@box/cli - npm Package Compare versions

Comparing version
4.6.0
to
4.7.0
LICENSE-THIRD-PARTY.txt

Sorry, the diff of this file is too big to display

+2
-1

@@ -12,3 +12,3 @@ {

],
"version": "4.6.0",
"version": "4.7.0",
"author": "Box <oss@box.com>",

@@ -88,2 +88,3 @@ "license": "Apache-2.0",

"/bin",
"/LICENSE-THIRD-PARTY.txt",
"/npm-shrinkwrap.json",

@@ -90,0 +91,0 @@ "/oclif.manifest.json",

@@ -6,2 +6,3 @@ 'use strict';

const _ = require('lodash');
const utilities = require('../../../util');

@@ -26,3 +27,3 @@ class EnvironmentsGetCommand extends BoxCommand {

} else {
await this.output(environment);
await this.output(utilities.maskObjectValuesByKey(environment));
}

@@ -36,3 +37,4 @@ }

EnvironmentsGetCommand.description = 'Get a Box environment';
EnvironmentsGetCommand.description =
'Get a Box environment or list all configured Box environments.\nclientSecret values are masked in CLI output. To view full secrets, access secure storage directly (for example, macOS Keychain, Windows Credential Manager, or a supported Linux equivalent).';

@@ -39,0 +41,0 @@ EnvironmentsGetCommand.flags = {

@@ -70,3 +70,3 @@ 'use strict';

await this.updateEnvironments(environmentsObject);
await this.output(environment);
await this.output(utilities.maskObjectValuesByKey(environment));
}

@@ -73,0 +73,0 @@ }

@@ -28,3 +28,23 @@ 'use strict';

const DEFAULT_ENVIRONMENT_NAME = 'oauth';
const OAUTH_CALLBACK_TIMEOUT_MS = 5 * 60 * 1000;
const LOOPBACK_HOST = 'localhost';
const DEFAULT_OPEN_AUTHORIZE_IN_BROWSER = (
openFn,
apps,
authorizeUrl,
useIncognito
) => {
if (useIncognito) {
openFn(authorizeUrl, {
newInstance: true,
app: { name: apps.browserPrivate },
});
} else {
openFn(authorizeUrl);
}
};
let oauthCallbackTimeoutMs = OAUTH_CALLBACK_TIMEOUT_MS;
let openAuthorizeInBrowser = DEFAULT_OPEN_AUTHORIZE_IN_BROWSER;
async function promptForPlatformAppCredentials(inquirerModule, clientId) {

@@ -115,3 +135,3 @@ if (!clientId) {

const port = flags.port;
const redirectUri = `http://localhost:${port}/callback`;
const redirectUri = `http://${LOOPBACK_HOST}:${port}/callback`;
const isUnsupportedDefaultAppPort = () =>

@@ -162,9 +182,2 @@ useDefaultBoxApp && !SUPPORTED_DEFAULT_APP_PORTS.includes(port);

if (isUnsupportedDefaultAppPort()) {
this.info(
chalk`{red Unsupported port "${port}" for the Official Box CLI app flow. Supported ports: ${SUPPORTED_DEFAULT_APP_PORTS.join(', ')}}`
);
return;
}
if (this.flags.reauthorize) {

@@ -211,9 +224,2 @@ // Keep the selected existing environment config for reauthorization.

if (isUnsupportedDefaultAppPort()) {
this.info(
chalk`{red Unsupported port "${port}" for the Official Box CLI app flow. Supported ports: ${SUPPORTED_DEFAULT_APP_PORTS.join(', ')}}`
);
return;
}
environment = {

@@ -228,2 +234,9 @@ clientId: answers.clientId,

if (isUnsupportedDefaultAppPort()) {
this.info(
chalk`{red Unsupported port "${port}" for the Official Box CLI app flow. Supported ports: ${SUPPORTED_DEFAULT_APP_PORTS.join(', ')}}`
);
return;
}
const environmentName = environment.name;

@@ -242,6 +255,72 @@ const sdkConfig = Object.freeze({

const app = express();
let callbackHandled = false;
// Keep run() blocked until callback flow completes.
// This prevents command exit before the OAuth redirect returns.
let resolveCallbackFlow;
const callbackFlowDone = new Promise((resolve) => {
resolveCallbackFlow = resolve;
});
let callbackTimeout;
// Timeout and callback may race, so teardown must be idempotent.
// This guard ensures cleanup and resolve happen only once.
let callbackFlowResolved = false;
let server;
try {
// Bind only to loopback to avoid exposing callback externally.
// Browser redirect is local, so external interfaces are unnecessary.
server = await new Promise((resolve, reject) => {
const s = app.listen(port, LOOPBACK_HOST);
s.once('listening', () => resolve(s));
s.once('error', reject);
});
} catch (error) {
if (error.code === 'EADDRINUSE') {
throw new BoxCLIError(
`Port ${port} is already in use. Please close the application using this port or use --port to specify a different port.`,
error
);
}
throw new BoxCLIError(
`Failed to start local OAuth server on port ${port}: ${error.message}`,
error
);
}
server = app.listen(port);
const shutdownServer = () => {
if (!server) {
return;
}
server.close();
if (typeof server.closeAllConnections === 'function') {
server.closeAllConnections();
}
};
// Use one finalize path so all exits apply the same cleanup.
// This keeps timeout and callback completion behavior consistent.
const finalizeCallbackFlow = () => {
if (callbackFlowResolved) {
return;
}
callbackFlowResolved = true;
clearTimeout(callbackTimeout);
shutdownServer();
resolveCallbackFlow();
};
// Bound callback wait time to avoid hanging sessions forever.
// If user abandons auth, the command exits predictably.
callbackTimeout = setTimeout(() => {
if (callbackHandled) {
return;
}
this.info(
chalk`{red Login timed out waiting for OAuth callback after ${oauthCallbackTimeoutMs / 1000} seconds.}`
);
finalizeCallbackFlow();
}, oauthCallbackTimeoutMs);
const state = nanoid(32);

@@ -251,2 +330,11 @@ const pkce = useDefaultBoxApp ? generatePKCE() : null;

app.get('/callback', async (request, res) => {
// Reject replayed callbacks after a completion was already accepted.
// This enforces single-use semantics for the local callback endpoint.
if (callbackHandled) {
res.status(409).send('OAuth callback already handled.');
return;
}
callbackHandled = true;
try {

@@ -276,3 +364,2 @@ if (request.query.state !== state) {

const client = sdk.getPersistentClient(tokenInfo, tokenCache);
const user = await client.users.get('me');

@@ -320,5 +407,7 @@

this.info(chalk`{red Login failed: ${errorMessage}}`);
res.status(500).send(
'Login failed. Please check the CLI output for details.'
);
} finally {
server.close();
server.closeAllConnections();
finalizeCallbackFlow();
}

@@ -367,13 +456,11 @@ });

http.get(
`http://localhost:${port}/callback?state=${authInfo.state}&code=${authInfo.code}`
`http://${LOOPBACK_HOST}:${port}/callback?state=${authInfo.state}&code=${authInfo.code}`
);
} else {
if (flags['incognito-browser']) {
open(authorizeUrl, {
newInstance: true,
app: { name: apps.browserPrivate },
});
} else {
open(authorizeUrl);
}
openAuthorizeInBrowser(
open,
apps,
authorizeUrl,
flags['incognito-browser']
);
this.info(

@@ -385,3 +472,3 @@ useDefaultBoxApp

}
await new Promise((resolve) => setTimeout(resolve, 1000));
await callbackFlowDone;
}

@@ -404,3 +491,5 @@ }

'\n' +
'Quickstart: run "box login -d" to sign in immediately. A browser window will open for authorization. Once access is granted, the environment is created and set as default — you can start running commands right away.';
'Quickstart: run "box login -d" to sign in immediately. A browser window will open for authorization. Once access is granted, the environment is created and set as default — you can start running commands right away.\n' +
'\n' +
'Headless environments: use --code (-c) if no browser is available. The CLI will display an authorize URL — visit it in an external browser, authorize and grant access to the app, then provide the state and authorization code back to the CLI when prompted.';

@@ -411,3 +500,4 @@ OAuthLoginCommand.flags = {

char: 'c',
description: 'Manually visit authorize URL and input code',
description:
'Manually provide state and authorization code instead of using a local callback server. Use this in headless environments where no browser is available — the CLI will display an authorize URL to visit externally.',
default: false,

@@ -459,2 +549,14 @@ }),

promptForPlatformAppCredentials,
setOAuthCallbackTimeoutMs(timeoutMs) {
oauthCallbackTimeoutMs = timeoutMs;
},
resetOAuthCallbackTimeoutMs() {
oauthCallbackTimeoutMs = OAUTH_CALLBACK_TIMEOUT_MS;
},
setOpenAuthorizeInBrowser(fn) {
openAuthorizeInBrowser = fn;
},
resetOpenAuthorizeInBrowser() {
openAuthorizeInBrowser = DEFAULT_OPEN_AUTHORIZE_IN_BROWSER;
},
};

@@ -23,2 +23,3 @@ 'use strict';

OSSLicensesCommand.noClient = true;
OSSLicensesCommand.description =

@@ -25,0 +26,0 @@ 'Print a list of open-source licensed packages used in the Box CLI';

@@ -33,2 +33,4 @@ 'use strict';

});
const DEFAULT_SECRET_KEY = 'clientSecret';
const DEFAULT_VISIBLE_SECRET_CHARS = 3;

@@ -303,2 +305,39 @@ /**

function maskSecret(secret, visibleChars = DEFAULT_VISIBLE_SECRET_CHARS) {
const normalizedVisibleChars =
_.isInteger(visibleChars) && visibleChars >= 0
? visibleChars
: DEFAULT_VISIBLE_SECRET_CHARS;
if (!_.isString(secret) || secret.length <= normalizedVisibleChars) {
return '*'.repeat(normalizedVisibleChars);
}
return `${'*'.repeat(secret.length - normalizedVisibleChars)}${secret.slice(-normalizedVisibleChars)}`;
}
function maskObjectValuesByKey(
value,
keyToMask = DEFAULT_SECRET_KEY,
visibleChars = DEFAULT_VISIBLE_SECRET_CHARS
) {
if (_.isArray(value)) {
return value.map((item) =>
maskObjectValuesByKey(item, keyToMask, visibleChars)
);
}
if (_.isPlainObject(value)) {
return _.mapValues(value, (objectValue, key) => {
if (key === keyToMask && !_.isNil(objectValue)) {
return maskSecret(objectValue, visibleChars);
}
return maskObjectValuesByKey(objectValue, keyToMask, visibleChars);
});
}
return value;
}
module.exports = {

@@ -393,2 +432,4 @@ /**

},
maskSecret,
maskObjectValuesByKey,
parseStringToObject,

@@ -395,0 +436,0 @@ checkDir,

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display