From a15916d0dc5341c884e8b06caa1a1059c9b3a621 Mon Sep 17 00:00:00 2001 From: Akanshu Aich Date: Mon, 17 Aug 2026 13:02:11 +0000 Subject: [PATCH 1/3] fix: added edx-lint version upgrade --- pylintrc | 11 +++++++++-- requirements/edx/development.txt | 2 +- requirements/edx/testing.txt | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/pylintrc b/pylintrc index 43f2b3bc9eb8..e1b1e841e147 100644 --- a/pylintrc +++ b/pylintrc @@ -64,7 +64,7 @@ # SERIOUSLY. # # ------------------------------ -# Generated by edx-lint version: 5.6.0 +# Generated by edx-lint version: 6.2.0 # ------------------------------ [MASTER] ignore = ,.git,.tox,migrations,node_modules,.pycharm_helpers @@ -257,6 +257,8 @@ enable = deprecated-pragma, unrecognized-inline-option, useless-suppression, + + pii-invalid-no-pii-annotation, disable = bad-indentation, broad-exception-raised, @@ -414,4 +416,9 @@ int-import-graph = [EXCEPTIONS] overgeneral-exceptions = builtins.Exception -# d6e4348dec0a8eb2752fc4fe02315286c298aeff +[PII] +pii-terms = + email, + username + +# e72ecbe0cf2f12ac71fdd2c33dc7bfb778f81c6e diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt index 90d1cb20fb5b..6c8bae228042 100644 --- a/requirements/edx/development.txt +++ b/requirements/edx/development.txt @@ -766,7 +766,7 @@ edx-i18n-tools==1.9.0 # -r requirements/edx/testing.txt # ora2 # xblocks-contrib -edx-lint==5.6.0 +edx-lint==6.2.0 # via -r requirements/edx/testing.txt edx-milestones==1.1.0 # via diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt index 0d2fd8b5e204..a9b7045f3d90 100644 --- a/requirements/edx/testing.txt +++ b/requirements/edx/testing.txt @@ -591,7 +591,7 @@ edx-i18n-tools==1.9.0 # -r requirements/edx/base.txt # ora2 # xblocks-contrib -edx-lint==5.6.0 +edx-lint==6.2.0 # via -r requirements/edx/testing.in edx-milestones==1.1.0 # via -r requirements/edx/base.txt From 4dd1dc34d71b5ecb36968b8e44940ded17c06c8f Mon Sep 17 00:00:00 2001 From: Akanshu Aich Date: Mon, 17 Aug 2026 13:24:47 +0000 Subject: [PATCH 2/3] fix: corrected edx-lint version installation --- requirements/edx/development.txt | 3 +-- requirements/edx/testing.in | 2 +- requirements/edx/testing.txt | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt index 6c8bae228042..72b76eb1d816 100644 --- a/requirements/edx/development.txt +++ b/requirements/edx/development.txt @@ -765,8 +765,7 @@ edx-i18n-tools==1.9.0 # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt # ora2 - # xblocks-contrib -edx-lint==6.2.0 +git+https://github.com/Akanshu-2u/edx-lint.git@aaich/BOMS-587-annotation#egg=edx-lint # via -r requirements/edx/testing.txt edx-milestones==1.1.0 # via diff --git a/requirements/edx/testing.in b/requirements/edx/testing.in index 14a0c781da82..6c4b4dffccb4 100644 --- a/requirements/edx/testing.in +++ b/requirements/edx/testing.in @@ -21,7 +21,7 @@ beautifulsoup4 # Library for extracting data from HTML and XML files code-annotations # Perform code annotation checking, such as for PII annotations cssselect # Used to extract HTML fragments via CSS selectors in 2 test cases and pyquery ddt # Run a test case multiple times with different input; used in many, many of our tests -edx-lint # pylint extensions for Open edX repositories +git+https://github.com/Akanshu-2u/edx-lint.git@master#egg=edx-lint # pylint extensions for Open edX repositories factory-boy # Library for creating test fixtures, used in many tests # Pinning the freezegun version because 0.3.13 is causing failures which have also been reported on the git repo by public. freezegun # Allows tests to mock the output of assorted datetime module functions diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt index a9b7045f3d90..d6e0743925e2 100644 --- a/requirements/edx/testing.txt +++ b/requirements/edx/testing.txt @@ -590,8 +590,7 @@ edx-i18n-tools==1.9.0 # via # -r requirements/edx/base.txt # ora2 - # xblocks-contrib -edx-lint==6.2.0 +git+https://github.com/Akanshu-2u/edx-lint.git@master#egg=edx-lint # pylint extensions for Open edX repositories # via -r requirements/edx/testing.in edx-milestones==1.1.0 # via -r requirements/edx/base.txt From 2add8ad278fbd543e44c46322233316cc390e3d5 Mon Sep 17 00:00:00 2001 From: Akanshu Aich Date: Tue, 18 Aug 2026 06:19:33 +0000 Subject: [PATCH 3/3] fix: added inline comment and correct annotation --- common/djangoapps/third_party_auth/models.py | 6 ++++++ lms/djangoapps/bulk_email/models.py | 2 ++ lms/djangoapps/course_goals/models.py | 1 + lms/djangoapps/courseware/models.py | 1 + openedx/core/djangoapps/catalog/models.py | 1 + .../djangoapps/content/course_overviews/models.py | 4 +++- openedx/core/djangoapps/credit/models.py | 14 +++++++++++--- .../djangoapps/django_comment_common/models.py | 1 + openedx/core/djangoapps/notifications/models.py | 3 +++ openedx/core/djangoapps/video_pipeline/models.py | 1 + 10 files changed, 30 insertions(+), 4 deletions(-) diff --git a/common/djangoapps/third_party_auth/models.py b/common/djangoapps/third_party_auth/models.py index 2a71b2d17d7f..b98b1994a9ee 100644 --- a/common/djangoapps/third_party_auth/models.py +++ b/common/djangoapps/third_party_auth/models.py @@ -681,17 +681,21 @@ class SAMLProviderConfig(ProviderConfig): default_last_name = models.CharField( max_length=255, blank=True, verbose_name="Default Value for Last Name", help_text="Default value for last name to be used if not present in SAML response.") + # pylint: disable-next=pii-invalid-no-pii-annotation # SAML attribute URN config, not a username value attr_username = models.CharField( max_length=128, blank=True, verbose_name="Username Hint Attribute", help_text="URN of SAML attribute to use as a suggested username for this user. Leave blank for default." ) + # pylint: disable-next=pii-invalid-no-pii-annotation # default value config, not a username value default_username = models.CharField( max_length=255, blank=True, verbose_name="Default Value for Username", help_text="Default value for username to be used if not present in SAML response." ) + # pylint: disable-next=pii-invalid-no-pii-annotation # SAML attribute URN config, not an email address attr_email = models.CharField( max_length=128, blank=True, verbose_name="Email Attribute", help_text="URN of SAML attribute containing the user's email address[es]. Leave blank for default.") + # pylint: disable-next=pii-invalid-no-pii-annotation # default value config, not an email address default_email = models.CharField( max_length=255, blank=True, verbose_name="Default Value for Email", help_text="Default value for email to be used if not present in SAML response." @@ -739,6 +743,7 @@ class SAMLProviderConfig(ProviderConfig): "for trusted providers that are known to provide accurate user information." ), ) + # pylint: disable-next=pii-invalid-no-pii-annotation # boolean toggle, not an email address skip_email_verification = models.BooleanField( default=True, help_text=_( @@ -761,6 +766,7 @@ class SAMLProviderConfig(ProviderConfig): "are skipped, their values are inferred as False (opted out)." ), ) + # pylint: disable-next=pii-invalid-no-pii-annotation # boolean toggle, not an email address disable_email_editing = models.BooleanField( default=False, help_text=_( diff --git a/lms/djangoapps/bulk_email/models.py b/lms/djangoapps/bulk_email/models.py index b1a7aa574408..0bf6b3f5e103 100644 --- a/lms/djangoapps/bulk_email/models.py +++ b/lms/djangoapps/bulk_email/models.py @@ -433,6 +433,7 @@ class Meta: course_id = CourseKeyField(max_length=255, db_index=True, unique=True) # Whether or not to enable instructor email + # pylint: disable-next=pii-invalid-no-pii-annotation # boolean toggle, not an email address email_enabled = models.BooleanField(default=False) @classmethod @@ -495,6 +496,7 @@ class BulkEmailFlag(ConfigurationModel): .. toggle_creation_date: 2016-05-05 """ # boolean field 'enabled' inherited from parent ConfigurationModel + # pylint: disable-next=pii-invalid-no-pii-annotation # boolean toggle, not an email address require_course_email_auth = models.BooleanField(default=True) @classmethod diff --git a/lms/djangoapps/course_goals/models.py b/lms/djangoapps/course_goals/models.py index 616966600040..039666931dd4 100644 --- a/lms/djangoapps/course_goals/models.py +++ b/lms/djangoapps/course_goals/models.py @@ -86,6 +86,7 @@ class Meta: verbose_name_plural = "Course goal reminder statuses" goal = models.OneToOneField(CourseGoal, on_delete=models.CASCADE, related_name='reminder_status') + # pylint: disable-next=pii-invalid-no-pii-annotation # boolean toggle, not an email address email_reminder_sent = models.BooleanField( default=False, help_text='Tracks if the email reminder to complete the Course Goal has been sent this week.' ) diff --git a/lms/djangoapps/courseware/models.py b/lms/djangoapps/courseware/models.py index 500629945141..1e51a3138eb3 100644 --- a/lms/djangoapps/courseware/models.py +++ b/lms/djangoapps/courseware/models.py @@ -564,6 +564,7 @@ class FinancialAssistanceConfiguration(ConfigurationModel): help_text=_('Financial Assistance Backend API Base URL.') ) + # pylint: disable-next=pii-invalid-no-pii-annotation # fixed service-account username, not a user's PII service_username = models.CharField( max_length=100, default='financial_assistance_service_user', diff --git a/openedx/core/djangoapps/catalog/models.py b/openedx/core/djangoapps/catalog/models.py index 98a012e3c365..b176da353e93 100644 --- a/openedx/core/djangoapps/catalog/models.py +++ b/openedx/core/djangoapps/catalog/models.py @@ -44,6 +44,7 @@ class CatalogIntegration(ConfigurationModel): ) ) + # pylint: disable-next=pii-invalid-no-pii-annotation # fixed service-account username, not a user's PII service_username = models.CharField( max_length=100, default='lms_catalog_service_user', diff --git a/openedx/core/djangoapps/content/course_overviews/models.py b/openedx/core/djangoapps/content/course_overviews/models.py index 6389350d6005..a53c9e9b1d14 100644 --- a/openedx/core/djangoapps/content/course_overviews/models.py +++ b/openedx/core/djangoapps/content/course_overviews/models.py @@ -57,7 +57,9 @@ class CourseOverview(TimeStampedModel): will cause a slew of modulestore reads as each course needs to be re-cached into the course overview. - .. no_pii: + .. pii: Contains proctoring_escalation_email, a staff contact address copied from CourseFields. + .. pii_types: email_address + .. pii_retirement: retained """ class Meta: diff --git a/openedx/core/djangoapps/credit/models.py b/openedx/core/djangoapps/credit/models.py index 9c14a15104b9..4b2db988e5f2 100644 --- a/openedx/core/djangoapps/credit/models.py +++ b/openedx/core/djangoapps/credit/models.py @@ -115,6 +115,7 @@ class CreditProvider(TimeStampedModel): ) ) + # pylint: disable-next=pii-invalid-no-pii-annotation # message template, not an email address eligibility_email_message = models.TextField( default="", help_text=gettext_lazy( @@ -124,6 +125,7 @@ class CreditProvider(TimeStampedModel): ) ) + # pylint: disable-next=pii-invalid-no-pii-annotation # message template, not an email address receipt_email_message = models.TextField( default="", help_text=gettext_lazy( @@ -424,7 +426,9 @@ class CreditRequirementStatus(TimeStampedModel): In case (3), no CreditRequirementStatus record will exist for the requirement and user. - .. no_pii: + .. pii: Contains username, anonymized by retire_user(). + .. pii_types: username + .. pii_retirement: local_api """ REQUIREMENT_STATUS_CHOICES = ( @@ -545,7 +549,9 @@ class CreditEligibility(TimeStampedModel): """ A record of a user's eligibility for credit for a specific course. - .. no_pii: + .. pii: Contains username, anonymized by retire_user(). + .. pii_types: username + .. pii_retirement: local_api """ username = models.CharField(max_length=255, db_index=True) course = models.ForeignKey(CreditCourse, related_name="eligibilities", on_delete=models.CASCADE) @@ -658,7 +664,9 @@ class CreditRequest(TimeStampedModel): (perhaps because the user did not finish filling in forms on the credit provider's site), the request record will be updated, but the UUID will remain the same. - .. no_pii: + .. pii: Contains username, anonymized by retire_user(). + .. pii_types: username + .. pii_retirement: local_api """ uuid = models.CharField(max_length=32, unique=True, db_index=True) diff --git a/openedx/core/djangoapps/django_comment_common/models.py b/openedx/core/djangoapps/django_comment_common/models.py index bd7b8fe66e67..3416748073fc 100644 --- a/openedx/core/djangoapps/django_comment_common/models.py +++ b/openedx/core/djangoapps/django_comment_common/models.py @@ -239,6 +239,7 @@ class CourseDiscussionSettings(models.Model): help_text="Key/value store mapping discussion IDs to discussion XBlock usage keys.", ) always_divide_inline_discussions = models.BooleanField(default=False) + # pylint: disable-next=pii-invalid-no-pii-annotation # boolean toggle, not an email address reported_content_email_notifications = models.BooleanField(default=False) _divided_discussions = models.TextField(db_column='divided_discussions', null=True, blank=True) # JSON list diff --git a/openedx/core/djangoapps/notifications/models.py b/openedx/core/djangoapps/notifications/models.py index b3a93e74e329..f852adbfaa48 100644 --- a/openedx/core/djangoapps/notifications/models.py +++ b/openedx/core/djangoapps/notifications/models.py @@ -146,6 +146,7 @@ class Notification(TimeStampedModel): content_context = models.JSONField(default=dict) content_url = models.URLField(null=True, blank=True) web = models.BooleanField(default=True, null=False, blank=False) + # pylint: disable-next=pii-invalid-no-pii-annotation # boolean toggle, not an email address email = models.BooleanField(default=False, null=False, blank=False) push = models.BooleanField(default=False, null=False, blank=False) last_read = models.DateTimeField(null=True, blank=True) @@ -184,7 +185,9 @@ class Meta: app = models.CharField(max_length=128, null=False, blank=False, db_index=True) web = models.BooleanField(default=True, null=False, blank=False) push = models.BooleanField(default=False, null=False, blank=False) + # pylint: disable-next=pii-invalid-no-pii-annotation # boolean toggle, not an email address email = models.BooleanField(default=False, null=False, blank=False) + # pylint: disable-next=pii-invalid-no-pii-annotation # cadence choice, not an email address email_cadence = models.CharField(max_length=64, choices=EmailCadenceChoices.choices, null=False, blank=False) is_active = models.BooleanField(default=True) diff --git a/openedx/core/djangoapps/video_pipeline/models.py b/openedx/core/djangoapps/video_pipeline/models.py index 1e21c19feef3..afeafaf1c604 100644 --- a/openedx/core/djangoapps/video_pipeline/models.py +++ b/openedx/core/djangoapps/video_pipeline/models.py @@ -38,6 +38,7 @@ class VEMPipelineIntegration(ConfigurationModel): help_text=_('video encode manager API URL.') ) + # pylint: disable-next=pii-invalid-no-pii-annotation # fixed service-account username, not a user's PII service_username = models.CharField( max_length=100, default='vem_service_user',