@@ -224,11 +224,8 @@ pub fn config_get(key: &str, raw: bool) -> Result<()> {
224224 let api_key = get_api_key ( ) ?;
225225 if raw {
226226 println ! ( "{}" , api_key) ;
227- } else if api_key. len ( ) > 8 {
228- let masked = format ! ( "{}***{}" , & api_key[ ..4 ] , & api_key[ api_key. len( ) - 4 ..] ) ;
229- println ! ( "{}" , masked) ;
230227 } else {
231- println ! ( "lin_***" ) ;
228+ println ! ( "{}" , mask_api_key_for_display ( & api_key ) ) ;
232229 }
233230 }
234231 "profile" => {
@@ -258,13 +255,7 @@ pub fn show_config() -> Result<()> {
258255 if let Some ( current) = & config. current {
259256 println ! ( "Current workspace: {}" , current) ;
260257 if let Some ( workspace) = config. workspaces . get ( current) {
261- let key = & workspace. api_key ;
262- if key. len ( ) > 12 {
263- let masked = format ! ( "{}...{}" , & key[ ..8 ] , & key[ key. len( ) - 4 ..] ) ;
264- println ! ( "API Key: {}" , masked) ;
265- } else {
266- println ! ( "API Key: {}" , key) ;
267- }
258+ println ! ( "API Key: {}" , mask_api_key_for_display( & workspace. api_key) ) ;
268259 }
269260 } else {
270261 println ! ( "No workspace configured. Run: linear workspace add <name>" ) ;
@@ -322,12 +313,7 @@ pub fn workspace_list() -> Result<()> {
322313 for ( name, workspace) in & config. workspaces {
323314 let is_current = config. current . as_ref ( ) == Some ( name) ;
324315 let marker = if is_current { "*" } else { " " } ;
325- let key = & workspace. api_key ;
326- let masked = if key. len ( ) > 12 {
327- format ! ( "{}...{}" , & key[ ..8 ] , & key[ key. len( ) - 4 ..] )
328- } else {
329- key. clone ( )
330- } ;
316+ let masked = mask_api_key_for_display ( & workspace. api_key ) ;
331317 println ! ( "{} {} ({})" , marker, name, masked) ;
332318 }
333319
@@ -360,13 +346,7 @@ pub fn workspace_current() -> Result<()> {
360346 if let Some ( current) = & config. current {
361347 println ! ( "Current workspace: {}" , current) ;
362348 if let Some ( workspace) = config. workspaces . get ( current) {
363- let key = & workspace. api_key ;
364- if key. len ( ) > 12 {
365- let masked = format ! ( "{}...{}" , & key[ ..8 ] , & key[ key. len( ) - 4 ..] ) ;
366- println ! ( "API Key: {}" , masked) ;
367- } else {
368- println ! ( "API Key: {}" , key) ;
369- }
349+ println ! ( "API Key: {}" , mask_api_key_for_display( & workspace. api_key) ) ;
370350 }
371351 } else {
372352 println ! ( "No workspace selected. Run: linear workspace add <name>" ) ;
@@ -422,7 +402,17 @@ fn oauth_config_has_secrets(oauth_config: &OAuthConfig) -> bool {
422402 . refresh_token
423403 . as_ref ( )
424404 . map ( |token| !token. is_empty ( ) )
425- . unwrap_or ( false )
405+ . unwrap_or ( false )
406+ }
407+
408+ fn mask_api_key_for_display ( api_key : & str ) -> String {
409+ if api_key. len ( ) > 12 {
410+ format ! ( "{}***{}" , & api_key[ ..4 ] , & api_key[ api_key. len( ) - 4 ..] )
411+ } else if api_key. starts_with ( "lin_" ) {
412+ "lin_***" . to_string ( )
413+ } else {
414+ "***" . to_string ( )
415+ }
426416}
427417
428418#[ cfg( feature = "secure-storage" ) ]
@@ -793,4 +783,22 @@ mod tests {
793783 assert ! ( ws. oauth. is_some( ) ) ;
794784 assert_eq ! ( ws. oauth. as_ref( ) . unwrap( ) . access_token, "oauth_tok" ) ;
795785 }
786+
787+ #[ test]
788+ fn test_mask_api_key_for_display_masks_short_linear_keys ( ) {
789+ assert_eq ! ( mask_api_key_for_display( "lin_short" ) , "lin_***" ) ;
790+ }
791+
792+ #[ test]
793+ fn test_mask_api_key_for_display_shows_prefix_and_suffix_for_long_keys ( ) {
794+ assert_eq ! (
795+ mask_api_key_for_display( "lin_api_prod123" ) ,
796+ "lin_***d123"
797+ ) ;
798+ }
799+
800+ #[ test]
801+ fn test_mask_api_key_for_display_masks_non_linear_short_keys ( ) {
802+ assert_eq ! ( mask_api_key_for_display( "secret" ) , "***" ) ;
803+ }
796804}
0 commit comments