@@ -36,24 +36,36 @@ function isConnectionStringEnvKey(key: string): boolean {
3636 ) ;
3737}
3838
39- // True when a connection-string value actually carries credentials: a URI with
40- // userinfo (scheme://user:pass@host/...) or embedded password material.
39+ // True when a connection-string value actually carries credentials: URI
40+ // userinfo of the form scheme://user:pass@host (a colon in the authority
41+ // before @, and before the next / or ?), or embedded password= material.
42+ // ADO-style DSNs carry Password= without a scheme, so the password check
43+ // does not require ://. An @ in the path or query (npm scopes, profile
44+ // URLs, email query params) is not userinfo.
4145function carriesConnectionCredentials ( value : string ) : boolean {
42- if ( ! value . includes ( "://" ) ) return false ;
43- return (
44- value . slice ( value . indexOf ( "://" ) + 3 ) . includes ( "@" ) ||
45- / p a s s w o r d = / i. test ( value )
46- ) ;
46+ return / p a s s w o r d = / i. test ( value ) || hasCredentialUserinfo ( value ) ;
47+ }
48+
49+ function hasCredentialUserinfo ( value : string ) : boolean {
50+ const schemeSep = value . indexOf ( "://" ) ;
51+ if ( schemeSep < 0 ) return false ;
52+ const rest = value . slice ( schemeSep + 3 ) ;
53+ const path = rest . indexOf ( "/" ) ;
54+ const query = rest . indexOf ( "?" ) ;
55+ let authorityEnd = rest . length ;
56+ if ( path >= 0 ) authorityEnd = Math . min ( authorityEnd , path ) ;
57+ if ( query >= 0 ) authorityEnd = Math . min ( authorityEnd , query ) ;
58+ const authority = rest . slice ( 0 , authorityEnd ) ;
59+ const at = authority . indexOf ( "@" ) ;
60+ if ( at < 0 ) return false ;
61+ return authority . slice ( 0 , at ) . includes ( ":" ) ;
4762}
4863
4964function isSecretEnvKey ( key : string , value : string ) : boolean {
50- if (
51- key === "API_KEY" ||
52- / (?: S E C R E T | T O K E N | P A S S W O R D | P R I V A T E | C R E D E N T I A L | A U T H ) / . test ( key )
53- ) {
65+ if ( / (?: S E C R E T | T O K E N | P A S S W O R D | P R I V A T E | C R E D E N T I A L | A U T H ) / . test ( key ) ) {
5466 return true ;
5567 }
56- // Bare-KEY class (OPENAI_KEY, STRIPE_KEY, *_API_KEY ): ENV-assignment scope
68+ // Bare-KEY class (OPENAI_KEY, STRIPE_KEY, API_KEY ): ENV-assignment scope
5769 // only. The (^|_) boundary keeps plain words ending in KEY (MONKEY) out.
5870 if ( / (?: ^ | _ ) K E Y $ / . test ( key ) ) return true ;
5971 if ( isConnectionStringEnvKey ( key ) ) return carriesConnectionCredentials ( value ) ;
@@ -91,8 +103,8 @@ export function scrubSecretShapedContent(text: string): string {
91103 ENV_ASSIGNMENT ,
92104 ( match , key : string , value : string ) => {
93105 if ( ! isSecretEnvKey ( key , value ) ) return match ;
94- const prefixEnd = match . lastIndexOf ( key ) ;
95- const prefix = match . slice ( 0 , prefixEnd ) ;
106+ const assignment = ` ${ key } = ${ value } ` ;
107+ const prefix = match . slice ( 0 , match . length - assignment . length ) ;
96108 return `${ prefix } ${ key } =${ CREDENTIAL_REDACTION } ` ;
97109 } ,
98110 ) ;
0 commit comments