Workflow reports included the authenticated user's personal email even when username/profile identity was sufficient (observed: 'Identity: jcwalker3 / [email protected]' in a reconciliation handoff). Add format_identity_summary() producing the canonical no-email identity line ('<username> / <profile>' with optional role/remote, reducing an email passed in the username slot to its local part), and assess_email_disclosure() flagging personal email addresses in report text unless the report itself justifies the disclosure (tool proof, explicit request, or identity disambiguation). Tests cover no-email summaries for author and reviewer identities, role/remote suffixes, email-in-username reduction, clean reports, single and multiple unjustified disclosures, and justified disambiguation. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
103 lines
3.9 KiB
Python
103 lines
3.9 KiB
Python
"""Identity-disclosure checks for workflow reports (#305).
|
|
|
|
Workflow reports sometimes included the authenticated user's personal
|
|
email even though username/profile identity is sufficient (observed:
|
|
``Identity: jcwalker3 / [email protected]`` in a reconciliation
|
|
handoff). These tests pin the no-email identity summary helper and the
|
|
final-report validator that flags unnecessary email disclosure.
|
|
"""
|
|
import sys
|
|
import unittest
|
|
|
|
sys.path.insert(0, str(__import__("pathlib").Path(__file__).resolve().parent.parent))
|
|
|
|
import review_proofs
|
|
|
|
|
|
class TestIdentitySummary(unittest.TestCase):
|
|
def test_author_summary_uses_username_and_profile_only(self):
|
|
summary = review_proofs.format_identity_summary(
|
|
"jcwalker3", "prgs-author")
|
|
self.assertEqual(summary, "jcwalker3 / prgs-author")
|
|
self.assertNotIn("@", summary)
|
|
|
|
def test_reviewer_summary_uses_username_and_profile_only(self):
|
|
summary = review_proofs.format_identity_summary(
|
|
"sysadmin", "prgs-reviewer")
|
|
self.assertEqual(summary, "sysadmin / prgs-reviewer")
|
|
|
|
def test_summary_appends_role_and_remote_without_email(self):
|
|
summary = review_proofs.format_identity_summary(
|
|
"jcwalker3", "prgs-author", role="author", remote="prgs")
|
|
self.assertIn("jcwalker3 / prgs-author", summary)
|
|
self.assertIn("author", summary)
|
|
self.assertIn("prgs", summary)
|
|
self.assertNotIn("@", summary)
|
|
|
|
def test_summary_never_leaks_email_passed_as_username(self):
|
|
"""Defense in depth: an email in the username slot is reduced."""
|
|
summary = review_proofs.format_identity_summary(
|
|
"[email protected]", "prgs-author")
|
|
self.assertNotIn("@", summary)
|
|
self.assertIn("jcwalker3", summary)
|
|
|
|
|
|
class TestEmailDisclosureAssessment(unittest.TestCase):
|
|
def test_report_without_email_passes(self):
|
|
report = "\n".join([
|
|
"Controller Handoff",
|
|
"Identity: jcwalker3 / prgs-author",
|
|
"Role: author",
|
|
])
|
|
res = review_proofs.assess_email_disclosure(report)
|
|
self.assertTrue(res["proven"])
|
|
self.assertFalse(res["flagged"])
|
|
self.assertEqual(res["emails"], [])
|
|
self.assertEqual(res["reasons"], [])
|
|
|
|
def test_unnecessary_email_is_flagged(self):
|
|
report = "\n".join([
|
|
"Controller Handoff",
|
|
"Identity: jcwalker3 / [email protected]",
|
|
])
|
|
res = review_proofs.assess_email_disclosure(report)
|
|
self.assertFalse(res["proven"])
|
|
self.assertTrue(res["flagged"])
|
|
self.assertIn("[email protected]", res["emails"])
|
|
self.assertTrue(res["reasons"])
|
|
self.assertFalse(res["justified"])
|
|
|
|
def test_multiple_emails_all_reported(self):
|
|
report = (
|
|
"Identity: [email protected] author\n"
|
|
"Reviewer: [email protected]\n"
|
|
)
|
|
res = review_proofs.assess_email_disclosure(report)
|
|
self.assertTrue(res["flagged"])
|
|
self.assertEqual(
|
|
sorted(res["emails"]),
|
|
["[email protected]", "[email protected]"],
|
|
)
|
|
|
|
def test_justified_email_with_explanation_is_not_flagged(self):
|
|
report = "\n".join([
|
|
"Identity: jcwalker3 / prgs-author",
|
|
"Contact email: [email protected]",
|
|
"Email required because two accounts share the username and the",
|
|
"address is necessary to disambiguate identity.",
|
|
])
|
|
res = review_proofs.assess_email_disclosure(report)
|
|
self.assertFalse(res["flagged"])
|
|
self.assertTrue(res["justified"])
|
|
self.assertIn("[email protected]", res["emails"])
|
|
|
|
def test_email_without_justification_language_is_flagged(self):
|
|
report = "Contact email: [email protected] just in case.\n"
|
|
res = review_proofs.assess_email_disclosure(report)
|
|
self.assertTrue(res["flagged"])
|
|
self.assertFalse(res["justified"])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|