@@ -23,6 +23,38 @@ export interface ExecOptions {
2323 neverThrow ?: boolean ;
2424}
2525
26+ // Long-form flags whose value carries a credential - the following arg (or inline
27+ // `--flag=value`) is replaced before args are logged so it never reaches log sinks.
28+ const REDACTED_FLAGS = new Set ( [
29+ "--password" ,
30+ "--token" ,
31+ "--secret" ,
32+ "--access-token" ,
33+ "--registry-token" ,
34+ "--registry-password" ,
35+ "--api-key" ,
36+ ] ) ;
37+
38+ export function redactArgsForLogging ( args ?: string [ ] ) : string [ ] | undefined {
39+ if ( ! args ) {
40+ return args ;
41+ }
42+
43+ return args . map ( ( arg , index ) => {
44+ const previous = index > 0 ? args [ index - 1 ] ?. trim ( ) : undefined ;
45+ if ( previous && REDACTED_FLAGS . has ( previous ) ) {
46+ return "[redacted]" ;
47+ }
48+
49+ const equalsIndex = arg . indexOf ( "=" ) ;
50+ if ( equalsIndex > 0 && REDACTED_FLAGS . has ( arg . slice ( 0 , equalsIndex ) . trim ( ) ) ) {
51+ return `${ arg . slice ( 0 , equalsIndex ) } =[redacted]` ;
52+ }
53+
54+ return arg ;
55+ } ) ;
56+ }
57+
2658export class Exec {
2759 private logger : SimpleStructuredLogger ;
2860 private abortSignal : AbortSignal | undefined ;
@@ -47,8 +79,15 @@ export class Exec {
4779 ) : Promise < Output > {
4880 const argsTrimmed = this . trimArgs ? args ?. map ( ( arg ) => arg . trim ( ) ) : args ;
4981
82+ const argsForLogging = redactArgsForLogging ( args ) ;
83+ const argsTrimmedForLogging = redactArgsForLogging ( argsTrimmed ) ;
84+
5085 const commandWithFirstArg = `${ command } ${ argsTrimmed ?. length ? ` ${ argsTrimmed [ 0 ] } ` : "" } ` ;
51- this . logger . debug ( `exec: ${ commandWithFirstArg } ` , { command, args, argsTrimmed } ) ;
86+ this . logger . debug ( `exec: ${ commandWithFirstArg } ` , {
87+ command,
88+ args : argsForLogging ,
89+ argsTrimmed : argsTrimmedForLogging ,
90+ } ) ;
5291
5392 const result = x ( command , argsTrimmed , {
5493 signal : opts ?. ignoreAbort ? undefined : this . abortSignal ,
@@ -60,8 +99,8 @@ export class Exec {
6099
61100 const metadata = {
62101 command,
63- argsRaw : args ,
64- argsTrimmed,
102+ argsRaw : argsForLogging ,
103+ argsTrimmed : argsTrimmedForLogging ,
65104 globalOpts : {
66105 trimArgs : this . trimArgs ,
67106 neverThrow : this . neverThrow ,
0 commit comments