Skip to content

Commit 3b02da1

Browse files
unity-cli@v1.8.2
1 parent 89e7372 commit 3b02da1

5 files changed

Lines changed: 347 additions & 13 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@rage-against-the-pixel/unity-cli",
3-
"version": "1.8.1",
3+
"version": "1.8.2",
44
"description": "A command line utility for the Unity Game Engine.",
55
"author": "RageAgainstThePixel",
66
"license": "MIT",

src/cli.ts

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ program.command('activate-license')
5050
Logger.instance.logLevel = LogLevel.DEBUG;
5151
}
5252

53-
Logger.instance.debug(JSON.stringify(options));
53+
Logger.instance.debugOptions(options);
5454

5555
const client = new LicensingClient();
5656
const licenseStr: string = options.license?.toString()?.trim();
@@ -79,6 +79,11 @@ program.command('activate-license')
7979
if (licenseType === LicenseType.professional && !options.serial) {
8080
options.serial = await PromptForSecretInput('Serial: ');
8181
}
82+
83+
// Mask credentials in CI environments before any potential logging
84+
Logger.instance.maskCredential(options.email);
85+
Logger.instance.maskCredential(options.password);
86+
Logger.instance.maskCredential(options.serial);
8287
}
8388

8489
const token = await client.Activate({
@@ -106,7 +111,7 @@ program.command('return-license')
106111
Logger.instance.logLevel = LogLevel.DEBUG;
107112
}
108113

109-
Logger.instance.debug(JSON.stringify(options));
114+
Logger.instance.debugOptions(options);
110115

111116
const client = new LicensingClient();
112117
const licenseStr: string = options.license?.toString()?.trim();
@@ -134,6 +139,9 @@ program.command('return-license')
134139
Logger.instance.error('Token is required when returning a floating license. Use -t or --token to specify it.');
135140
process.exit(1);
136141
}
142+
143+
// Mask token in CI environments before any potential logging
144+
Logger.instance.maskCredential(token);
137145
}
138146

139147
await client.Deactivate(licenseType, token);
@@ -220,7 +228,7 @@ program.command('hub-install')
220228
Logger.instance.logLevel = LogLevel.DEBUG;
221229
}
222230

223-
Logger.instance.debug(JSON.stringify(options));
231+
Logger.instance.debugOptions(options);
224232

225233
if (options.autoUpdate === true && options.hubVersion) {
226234
Logger.instance.error('Cannot use --auto-update with --hub-version.');
@@ -252,7 +260,7 @@ program.command('hub')
252260
Logger.instance.logLevel = LogLevel.DEBUG;
253261
}
254262

255-
Logger.instance.debug(JSON.stringify({ args, options }));
263+
Logger.instance.debugOptions({ args, options });
256264

257265
const unityHub = new UnityHub();
258266
const output = await unityHub.Exec(args, { silent: false, showCommand: Logger.instance.logLevel === LogLevel.DEBUG });
@@ -280,7 +288,7 @@ program.command('setup-unity')
280288
Logger.instance.logLevel = LogLevel.DEBUG;
281289
}
282290

283-
Logger.instance.debug(JSON.stringify(options));
291+
Logger.instance.debugOptions(options);
284292

285293
let unityProject: UnityProject | undefined;
286294

@@ -373,7 +381,7 @@ program.command('uninstall-unity')
373381
Logger.instance.logLevel = LogLevel.DEBUG;
374382
}
375383

376-
Logger.instance.debug(JSON.stringify(options));
384+
Logger.instance.debugOptions(options);
377385

378386
let unityEditor: UnityEditor | undefined;
379387
const unityVersionStr = options.unityVersion?.toString()?.trim();
@@ -472,7 +480,7 @@ program.command('run')
472480
Logger.instance.logLevel = requestedLogLevel;
473481
}
474482

475-
Logger.instance.debug(JSON.stringify({ options, args }));
483+
Logger.instance.debugOptions({ options, args });
476484

477485
let unityEditor: UnityEditor | undefined;
478486
const editorPath = options.unityEditor?.toString()?.trim() || process.env.UNITY_EDITOR_PATH || undefined;
@@ -540,7 +548,7 @@ program.command('list-project-templates')
540548
Logger.instance.logLevel = LogLevel.DEBUG;
541549
}
542550

543-
Logger.instance.debug(JSON.stringify(options));
551+
Logger.instance.debugOptions(options);
544552

545553
const unityVersionStr = options.unityVersion?.toString()?.trim();
546554

@@ -593,7 +601,7 @@ program.command('create-project')
593601
Logger.instance.logLevel = LogLevel.DEBUG;
594602
}
595603

596-
Logger.instance.debug(JSON.stringify(options));
604+
Logger.instance.debugOptions(options);
597605

598606
const unityVersionStr = options.unityVersion?.toString()?.trim();
599607

@@ -665,7 +673,7 @@ program.command('open-project')
665673
Logger.instance.logLevel = LogLevel.DEBUG;
666674
}
667675

668-
Logger.instance.debug(JSON.stringify(options));
676+
Logger.instance.debugOptions(options);
669677
const projectPath = options.unityProject?.toString()?.trim() || process.env.UNITY_PROJECT_PATH || undefined;
670678
const unityProject = await UnityProject.GetProject(projectPath);
671679

@@ -731,7 +739,7 @@ program.command('sign-package')
731739
Logger.instance.logLevel = LogLevel.DEBUG;
732740
}
733741

734-
Logger.instance.debug(JSON.stringify(options));
742+
Logger.instance.debugOptions(options);
735743

736744
const packagePath = path.normalize(options.package?.toString()?.trim());
737745

@@ -796,6 +804,11 @@ program.command('sign-package')
796804
process.exit(1);
797805
}
798806

807+
// Mask credentials in CI environments before any potential logging
808+
Logger.instance.maskCredential(username);
809+
Logger.instance.maskCredential(password);
810+
Logger.instance.maskCredential(organization);
811+
799812
// must use a unity editor 6000.3 or newer
800813
const unityVersion = new UnityVersion('6000.3');
801814
const unityHub = new UnityHub();

src/logging.ts

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,83 @@ export class Logger {
225225
}
226226
}
227227

228+
/**
229+
* Masks a credential value in CI environments before it appears in logs.
230+
* This is a convenience wrapper around CI_mask for credential values.
231+
* @param value The credential value to mask.
232+
*/
233+
public maskCredential(value: string | undefined): void {
234+
if (value && value.length > 0) {
235+
this.CI_mask(value);
236+
}
237+
}
238+
239+
/**
240+
* Logs command-line options with sensitive information scrubbed.
241+
* Automatically removes passwords, tokens, emails, and other credentials from the output.
242+
* @param options The options object to log (typically from commander.js).
243+
* @param optionalParams Additional parameters to log.
244+
*/
245+
public debugOptions(options: any, ...optionalParams: any[]): void {
246+
const scrubbed = this.scrubSensitiveData(options);
247+
this.debug(JSON.stringify(scrubbed), ...optionalParams);
248+
}
249+
250+
/**
251+
* List of sensitive option keys that should be scrubbed from debug output.
252+
*/
253+
private readonly SENSITIVE_KEYS = [
254+
'password',
255+
'email',
256+
'serial',
257+
'token',
258+
'config',
259+
'organization',
260+
'username',
261+
'servicesConfig'
262+
];
263+
264+
/**
265+
* Scrubs sensitive information from an object for safe logging.
266+
* Creates a deep clone of the object and replaces sensitive values with [REDACTED].
267+
* @param obj The object to scrub (typically command-line options).
268+
* @returns A new object with sensitive values replaced.
269+
*/
270+
private scrubSensitiveData(obj: any): any {
271+
if (obj === null || obj === undefined) {
272+
return obj;
273+
}
274+
275+
if (typeof obj !== 'object') {
276+
return obj;
277+
}
278+
279+
if (Array.isArray(obj)) {
280+
return obj.map((item: any) => this.scrubSensitiveData(item));
281+
}
282+
283+
const scrubbedObj: any = {};
284+
285+
for (const key in obj) {
286+
if (obj.hasOwnProperty(key)) {
287+
const lowerKey = key.toLowerCase();
288+
const isSensitive = this.SENSITIVE_KEYS.some(
289+
sensitiveKey => lowerKey.includes(sensitiveKey.toLowerCase())
290+
);
291+
292+
if (isSensitive) {
293+
scrubbedObj[key] = '[REDACTED]';
294+
} else if (typeof obj[key] === 'object') {
295+
scrubbedObj[key] = this.scrubSensitiveData(obj[key]);
296+
} else {
297+
scrubbedObj[key] = obj[key];
298+
}
299+
}
300+
}
301+
302+
return scrubbedObj;
303+
}
304+
228305
/**
229306
* Sets an environment variable in CI environments that support it.
230307
* @param name The name of the environment variable.

src/unity-editor.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,29 @@ export class UnityEditor {
191191
return templates;
192192
}
193193

194+
/**
195+
* Scrubs sensitive command-line arguments for safe logging.
196+
* Replaces values for sensitive flags like -username, -password, etc. with [REDACTED].
197+
* @param args The command-line arguments array.
198+
* @returns A new array with sensitive values redacted.
199+
*/
200+
private scrubSensitiveArgs(args: string[]): string[] {
201+
const sensitiveFlags = ['-username', '-password', '-cloudOrganization', '-serial'];
202+
const scrubbedArgs: string[] = [];
203+
204+
for (let i = 0; i < args.length; i++) {
205+
scrubbedArgs.push(args[i]);
206+
207+
// If this is a sensitive flag and the next item is its value
208+
if (sensitiveFlags.includes(args[i]) && i + 1 < args.length) {
209+
scrubbedArgs.push('[REDACTED]');
210+
i++; // Skip the next item (the actual value) since we've already added [REDACTED]
211+
}
212+
}
213+
214+
return scrubbedArgs;
215+
}
216+
194217
/**
195218
* Run the Unity Editor with the specified command line arguments.
196219
* @param command The command containing arguments and optional project path.
@@ -264,7 +287,10 @@ export class UnityEditor {
264287

265288
const logPath: string = GetArgumentValueAsString('-logFile', command.args);
266289
logTail = TailLogFile(logPath, command.projectPath);
267-
const commandStr = `\x1b[34m${this.editorPath} ${command.args.join(' ')}\x1b[0m`;
290+
291+
// Scrub sensitive arguments before logging
292+
const scrubbedArgs = this.scrubSensitiveArgs(command.args);
293+
const commandStr = `\x1b[34m${this.editorPath} ${scrubbedArgs.join(' ')}\x1b[0m`;
268294
this.logger.startGroup(commandStr);
269295

270296
if (this.version.isLegacy() && process.platform === 'darwin' && process.arch === 'arm64') {

0 commit comments

Comments
 (0)