From c355c4819ff2efc3195406424c66ff97fbdb14a1 Mon Sep 17 00:00:00 2001 From: Saurya Velagapudi Date: Wed, 4 Feb 2026 09:30:02 -0800 Subject: [PATCH] feat(recaptcha): add assessment name to logging and AssessmentResult (#12744) Co-authored-by: openhands --- enterprise/server/auth/recaptcha_service.py | 7 +++++++ .../tests/unit/test_recaptcha_service.py | 18 ++++++++++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/enterprise/server/auth/recaptcha_service.py b/enterprise/server/auth/recaptcha_service.py index 1afce90af9..b839e87868 100644 --- a/enterprise/server/auth/recaptcha_service.py +++ b/enterprise/server/auth/recaptcha_service.py @@ -18,6 +18,7 @@ from openhands.core.logger import openhands_logger as logger class AssessmentResult: """Result of a reCAPTCHA Enterprise assessment.""" + name: str score: float valid: bool action_valid: bool @@ -100,6 +101,10 @@ class RecaptchaService: response = self.client.create_assessment(request) + # Capture assessment name for potential annotation later + # Format: projects/{project_id}/assessments/{assessment_id} + assessment_name = response.name + token_properties = response.token_properties risk_analysis = response.risk_analysis @@ -129,6 +134,7 @@ class RecaptchaService: logger.info( 'recaptcha_assessment', extra={ + 'assessment_name': assessment_name, 'score': score, 'valid': valid, 'action_valid': action_valid, @@ -141,6 +147,7 @@ class RecaptchaService: ) return AssessmentResult( + name=assessment_name, score=score, valid=valid, action_valid=action_valid, diff --git a/enterprise/tests/unit/test_recaptcha_service.py b/enterprise/tests/unit/test_recaptcha_service.py index 91ab441aa1..4758d4ff48 100644 --- a/enterprise/tests/unit/test_recaptcha_service.py +++ b/enterprise/tests/unit/test_recaptcha_service.py @@ -94,6 +94,7 @@ class TestRecaptchaServiceCreateAssessment: """Test that assessment allows request when score is above threshold.""" # Arrange mock_response = MagicMock() + mock_response.name = 'projects/test-project/assessments/abc123' mock_response.token_properties.valid = True mock_response.token_properties.action = 'LOGIN' mock_response.risk_analysis.score = 0.9 @@ -110,6 +111,7 @@ class TestRecaptchaServiceCreateAssessment: # Assert assert isinstance(result, AssessmentResult) + assert result.name == 'projects/test-project/assessments/abc123' assert result.allowed is True assert result.score == 0.9 assert result.valid is True @@ -122,6 +124,7 @@ class TestRecaptchaServiceCreateAssessment: """Test that assessment blocks request when score is below threshold.""" # Arrange mock_response = MagicMock() + mock_response.name = 'projects/test-project/assessments/def456' mock_response.token_properties.valid = True mock_response.token_properties.action = 'LOGIN' mock_response.risk_analysis.score = 0.2 @@ -146,6 +149,7 @@ class TestRecaptchaServiceCreateAssessment: """Test that assessment blocks request when token is invalid.""" # Arrange mock_response = MagicMock() + mock_response.name = 'projects/test-project/assessments/ghi789' mock_response.token_properties.valid = False mock_response.token_properties.action = 'LOGIN' mock_response.risk_analysis.score = 0.9 @@ -170,6 +174,7 @@ class TestRecaptchaServiceCreateAssessment: """Test that assessment blocks request when action doesn't match.""" # Arrange mock_response = MagicMock() + mock_response.name = 'projects/test-project/assessments/jkl012' mock_response.token_properties.valid = True mock_response.token_properties.action = 'SIGNUP' mock_response.risk_analysis.score = 0.9 @@ -194,6 +199,7 @@ class TestRecaptchaServiceCreateAssessment: """Test that email is included in user_info when provided.""" # Arrange mock_response = MagicMock() + mock_response.name = 'projects/test-project/assessments/mno345' mock_response.token_properties.valid = True mock_response.token_properties.action = 'LOGIN' mock_response.risk_analysis.score = 0.9 @@ -223,6 +229,7 @@ class TestRecaptchaServiceCreateAssessment: """Test that user_info is not included when email is None.""" # Arrange mock_response = MagicMock() + mock_response.name = 'projects/test-project/assessments/pqr678' mock_response.token_properties.valid = True mock_response.token_properties.action = 'LOGIN' mock_response.risk_analysis.score = 0.9 @@ -248,10 +255,13 @@ class TestRecaptchaServiceCreateAssessment: # If user_info exists, verify account_id is empty (not set) assert not assessment.event.user_info.account_id - def test_should_log_assessment_details(self, recaptcha_service, mock_gcp_client): - """Test that assessment details are logged.""" + def test_should_log_assessment_details_including_name( + self, recaptcha_service, mock_gcp_client + ): + """Test that assessment details including assessment name are logged.""" # Arrange mock_response = MagicMock() + mock_response.name = 'projects/test-project/assessments/stu901' mock_response.token_properties.valid = True mock_response.token_properties.action = 'LOGIN' mock_response.risk_analysis.score = 0.9 @@ -271,6 +281,10 @@ class TestRecaptchaServiceCreateAssessment: mock_logger.info.assert_called_once() call_kwargs = mock_logger.info.call_args assert call_kwargs[0][0] == 'recaptcha_assessment' + assert ( + call_kwargs[1]['extra']['assessment_name'] + == 'projects/test-project/assessments/stu901' + ) assert call_kwargs[1]['extra']['score'] == 0.9 assert call_kwargs[1]['extra']['valid'] is True assert call_kwargs[1]['extra']['action_valid'] is True