188188 rf"\A(?:{ SENSITIVE_CONTAINER_KEY } )\Z" ,
189189 re .IGNORECASE ,
190190)
191+ SENSITIVE_OPTION_KEY_RE : Final [re .Pattern [str ]] = re .compile (
192+ r"\A(?:(?:[a-z0-9]+[-_.])*(?:access[-_]?key(?:[-_]?id)?|access[-_]?token|api[-_]?key|apikey|"
193+ r"auth[-_]?token|client[-_]?secret|credential|password|passwd|private[-_]?key|pwd|"
194+ r"refresh[-_]?token|sas|secret|secret[-_]?key|signature|sig|token)|authorization)\Z" ,
195+ re .IGNORECASE ,
196+ )
191197PYTHON_COMPOUND_ASSIGNMENT_OPERATORS : Final [tuple [str , ...]] = (
192198 "**=" ,
193199 "//=" ,
@@ -714,6 +720,15 @@ def _argument_keyword_and_value(
714720 return None , significant
715721
716722
723+ def _is_sensitive_literal_key (key : object ) -> bool :
724+ if isinstance (key , bytes ):
725+ try :
726+ key = key .decode ()
727+ except UnicodeDecodeError :
728+ return False
729+ return isinstance (key , str ) and SENSITIVE_CONTAINER_KEY_RE .fullmatch (key ) is not None
730+
731+
717732def _literal_sensitive_key (tokens : list [tokenize .TokenInfo ]) -> bool :
718733 significant = _significant_tokens (tokens )
719734 if len (significant ) != 1 or significant [0 ].type != tokenize .STRING :
@@ -722,12 +737,35 @@ def _literal_sensitive_key(tokens: list[tokenize.TokenInfo]) -> bool:
722737 key = ast .literal_eval (significant [0 ].string )
723738 except (SyntaxError , ValueError ):
724739 return False
725- if isinstance (key , bytes ):
726- try :
727- key = key .decode ()
728- except UnicodeDecodeError :
729- return False
730- return isinstance (key , str ) and SENSITIVE_CONTAINER_KEY_RE .fullmatch (key ) is not None
740+ return _is_sensitive_literal_key (key )
741+
742+
743+ def _target_contains_sensitive_literal_key (target : str ) -> bool :
744+ try :
745+ expression = ast .parse (target .strip (), mode = "eval" ).body
746+ except (SyntaxError , ValueError ):
747+ return False
748+ if isinstance (expression , ast .Constant ):
749+ return _is_sensitive_literal_key (expression .value )
750+ for node in ast .walk (expression ):
751+ if (
752+ isinstance (node , ast .Subscript )
753+ and isinstance (node .slice , ast .Constant )
754+ and _is_sensitive_literal_key (node .slice .value )
755+ ):
756+ return True
757+ return False
758+
759+
760+ def _literal_sensitive_option (tokens : list [tokenize .TokenInfo ]) -> bool :
761+ significant = _significant_tokens (tokens )
762+ if len (significant ) != 1 or significant [0 ].type != tokenize .STRING :
763+ return False
764+ try :
765+ option = ast .literal_eval (significant [0 ].string )
766+ except (SyntaxError , ValueError ):
767+ return False
768+ return isinstance (option , str ) and SENSITIVE_OPTION_KEY_RE .fullmatch (option .lstrip ("-" )) is not None
731769
732770
733771def _comparison_target_start (
@@ -789,9 +827,9 @@ def _redact_sensitive_keyed_calls(text: str) -> str:
789827 }
790828 replacements : list [tuple [int , int ]] = []
791829 for index , token in enumerate (tokens ):
792- argument_spec = keyed_call_arguments .get (token .string ) if token .type == tokenize .NAME else None
793- if argument_spec is None :
830+ if token .type != tokenize .NAME :
794831 continue
832+ argument_spec = keyed_call_arguments .get (token .string )
795833 open_paren_index = index + 1
796834 while open_paren_index < len (tokens ) and tokens [open_paren_index ].type in {tokenize .NL , tokenize .COMMENT }:
797835 open_paren_index += 1
@@ -807,6 +845,33 @@ def _redact_sensitive_keyed_calls(text: str) -> str:
807845 _argument_keyword_and_value (tokens [argument_start :argument_end ])
808846 for argument_start , argument_end in argument_ranges
809847 ]
848+ for keyword , argument_value_tokens in arguments :
849+ if keyword == "auth" and argument_value_tokens :
850+ replacements .append (
851+ (
852+ _position_offset (offsets , argument_value_tokens [0 ].start , len (text )),
853+ _position_offset (offsets , argument_value_tokens [- 1 ].end , len (text )),
854+ )
855+ )
856+
857+ if token .string in {"add_argument" , "option" }:
858+ default_tokens = next (
859+ (argument_value_tokens for keyword , argument_value_tokens in arguments if keyword == "default" ),
860+ None ,
861+ )
862+ positional_arguments = [
863+ argument_value_tokens for keyword , argument_value_tokens in arguments if keyword is None
864+ ]
865+ if default_tokens and any (_literal_sensitive_option (value_tokens ) for value_tokens in positional_arguments ):
866+ replacements .append (
867+ (
868+ _position_offset (offsets , default_tokens [0 ].start , len (text )),
869+ _position_offset (offsets , default_tokens [- 1 ].end , len (text )),
870+ )
871+ )
872+
873+ if argument_spec is None :
874+ continue
810875 key_index , value_index , key_keywords , value_keywords = argument_spec
811876 key_tokens = _keyed_call_argument (arguments , key_index , key_keywords )
812877 value_tokens = _keyed_call_argument (arguments , value_index , value_keywords )
@@ -954,11 +1019,16 @@ def _redact_python_expression_assignments(text: str) -> str:
9541019 target_start = _position_offset (offsets , tokens [target_start_index ].start , text_length )
9551020 target_end = _position_offset (offsets , token .start , text_length )
9561021 target = text [target_start :target_end ]
1022+ source_target_is_sensitive = SENSITIVE_ASSIGNMENT_TARGET_RE .search (target ) is not None
1023+ literal_target_is_sensitive = _target_contains_sensitive_literal_key (target )
9571024 if token .string == ":" :
958- if QUOTED_SENSITIVE_MAPPING_KEY_RE .search (target ) is None :
1025+ source_target_is_sensitive = QUOTED_SENSITIVE_MAPPING_KEY_RE .search (target ) is not None
1026+ if not source_target_is_sensitive and not literal_target_is_sensitive :
9591027 continue
960- elif SENSITIVE_ASSIGNMENT_TARGET_RE .search (target ) is None and not (
961- "," in target and _contains_sensitive_unpacking_target (target )
1028+ elif (
1029+ not source_target_is_sensitive
1030+ and not literal_target_is_sensitive
1031+ and not ("," in target and _contains_sensitive_unpacking_target (target ))
9621032 ):
9631033 continue
9641034
@@ -984,7 +1054,10 @@ def _redact_python_expression_assignments(text: str) -> str:
9841054 )
9851055 is_annotated_target = ANNOTATED_SENSITIVE_ASSIGNMENT_TARGET_RE .search (target ) is not None
9861056 requires_token_redaction = (
987- token .string == ":=" or "," in target or PREFIXED_QUOTED_SENSITIVE_MAPPING_KEY_RE .search (target ) is not None
1057+ token .string == ":="
1058+ or "," in target
1059+ or PREFIXED_QUOTED_SENSITIVE_MAPPING_KEY_RE .search (target ) is not None
1060+ or (literal_target_is_sensitive and not source_target_is_sensitive )
9881061 )
9891062 if (
9901063 is_simple_value
0 commit comments