diff --git a/deploy/coven-github/__pycache__/coven_github_adapter.cpython-312.pyc b/deploy/coven-github/__pycache__/coven_github_adapter.cpython-312.pyc new file mode 100644 index 0000000..c98d55b Binary files /dev/null and b/deploy/coven-github/__pycache__/coven_github_adapter.cpython-312.pyc differ diff --git a/deploy/coven-github/__pycache__/test_coven_github_adapter.cpython-312.pyc b/deploy/coven-github/__pycache__/test_coven_github_adapter.cpython-312.pyc new file mode 100644 index 0000000..92d661f Binary files /dev/null and b/deploy/coven-github/__pycache__/test_coven_github_adapter.cpython-312.pyc differ diff --git a/deploy/coven-github/__pycache__/test_github_token_redaction.cpython-312.pyc b/deploy/coven-github/__pycache__/test_github_token_redaction.cpython-312.pyc new file mode 100644 index 0000000..4e52123 Binary files /dev/null and b/deploy/coven-github/__pycache__/test_github_token_redaction.cpython-312.pyc differ diff --git a/deploy/coven-github/coven_github_adapter.py b/deploy/coven-github/coven_github_adapter.py index d066c4b..2ae2a2c 100644 --- a/deploy/coven-github/coven_github_adapter.py +++ b/deploy/coven-github/coven_github_adapter.py @@ -1652,16 +1652,11 @@ def redacted_command_result(result): def redact_tokenish(text): if not text: return text - markers = ["ghs_", "ghu_", "github_pat_", "x-access-token:"] - redacted = text - for marker in markers: - while marker in redacted: - index = redacted.find(marker) - end = index + len(marker) - while end < len(redacted) and redacted[end] not in " \n\r\t'\"": - end += 1 - redacted = redacted[:index] + marker + "[redacted]" + redacted[end:] - return redacted + return re.sub( + r"(github_pat_|ghp_|gho_|ghr_|ghs_|ghu_|x-access-token:)[A-Za-z0-9_]+", + r"\1[redacted]", + text, + ) def redact_secrets(text, secret_values=()): diff --git a/deploy/coven-github/test_github_token_redaction.py b/deploy/coven-github/test_github_token_redaction.py new file mode 100644 index 0000000..82542e4 --- /dev/null +++ b/deploy/coven-github/test_github_token_redaction.py @@ -0,0 +1,50 @@ +import importlib.util +import unittest +from pathlib import Path + + +def load_adapter(): + path = Path(__file__).with_name("coven_github_adapter.py") + spec = importlib.util.spec_from_file_location("coven_github_adapter", path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +_adapter = load_adapter() +redact_secrets = _adapter.redact_secrets +redact_tokenish = _adapter.redact_tokenish + +class GithubTokenRedactionTests(unittest.TestCase): + def test_redact_tokenish_covers_all_supported_github_token_families(self): + tokens = [ + "ghp_" + "p" * 36, + "gho_" + "o" * 36, + "ghr_" + "r" * 36, + "ghs_" + "s" * 36, + "ghu_" + "u" * 36, + "github_pat_" + "a" * 82, + ] + payload = " ".join(tokens) + redacted = redact_tokenish(payload) + + for token in tokens: + self.assertNotIn(token, redacted) + for prefix in ("ghp_", "gho_", "ghr_", "ghs_", "ghu_", "github_pat_"): + self.assertIn(prefix + "[redacted]", redacted) + + def test_redact_secrets_covers_classic_token_families_in_publication_text(self): + tokens = [ + "ghp_" + "p" * 36, + "gho_" + "o" * 36, + "ghr_" + "r" * 36, + ] + redacted = redact_secrets("diagnostic: " + "|".join(tokens)) + + for token in tokens: + self.assertNotIn(token, redacted) + self.assertEqual(redacted.count("[redacted]"), len(tokens)) + + +if __name__ == "__main__": + unittest.main()