Skip to content

Commit ea02944

Browse files
committed
fix(spp_approval,spp_dci_client): prevent RPC exposure of system approval and narrow exception handling
Rename action_approve_system to _action_approve_system to prevent Odoo from exposing it via the RPC interface, since it uses sudo() and skips _check_can_approve() authorization checks. Replace broad Exception catches in spp_dci_client with specific exception types (json.JSONDecodeError, KeyError, TypeError) and add json import.
1 parent a7d70b2 commit ea02944

2 files changed

Lines changed: 9 additions & 5 deletions

File tree

spp_approval/models/approval_mixin.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ def action_approve(self, comment=None):
332332
record._check_can_approve()
333333
record._do_approve(comment=comment)
334334

335-
def action_approve_system(self, comment=None):
335+
def _action_approve_system(self, comment=None):
336336
"""System-initiated approval bypassing user permission checks.
337337
338338
Use this for automated approvals triggered by system events (e.g., DCI
@@ -341,6 +341,9 @@ def action_approve_system(self, comment=None):
341341
This method uses sudo() to bypass access controls and skips the
342342
_check_can_approve() permission validation.
343343
344+
The underscore prefix is intentional — it prevents this method from being
345+
callable via Odoo's RPC interface, since it must not be exposed to users.
346+
344347
Args:
345348
comment: Optional approval comment for audit trail
346349
"""
@@ -392,7 +395,7 @@ def _do_approve(self, comment=None, auto=False):
392395
)
393396

394397
if not self.env.cr.fetchone():
395-
raise UserError(_("This record was modified by another user. " "Please refresh and try again."))
398+
raise UserError(_("This record was modified by another user. Please refresh and try again."))
396399
else:
397400
# ORM approach for computed/non-stored approval_state
398401
# Verify the record is in pending state (via computed field)
@@ -416,7 +419,7 @@ def _do_approve(self, comment=None, auto=False):
416419
)
417420

418421
if not self.env.cr.fetchone():
419-
raise UserError(_("This record was modified by another user. " "Please refresh and try again."))
422+
raise UserError(_("This record was modified by another user. Please refresh and try again."))
420423

421424
# Invalidate cache
422425
self.invalidate_recordset()

spp_dci_client/services/client.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""DCI Client Service for making signed API requests."""
22

3+
import json
34
import logging
45
import time
56
import uuid
@@ -943,7 +944,7 @@ def _make_request(self, endpoint: str, envelope: dict, _retry_auth: bool = True)
943944
log_error_detail = "401 Unauthorized - retrying with fresh token"
944945
try:
945946
log_response_data = response.json()
946-
except Exception:
947+
except json.JSONDecodeError:
947948
log_response_data = None
948949
self.data_source.clear_oauth2_token_cache()
949950
return self._make_request(endpoint, envelope, _retry_auth=False)
@@ -979,7 +980,7 @@ def _make_request(self, endpoint: str, envelope: dict, _retry_auth: bool = True)
979980
technical_detail += f": {error_data['header']['status_reason_message']}"
980981
else:
981982
technical_detail += f": {response_text}"
982-
except Exception:
983+
except (json.JSONDecodeError, KeyError, TypeError):
983984
technical_detail += f": {response_text}"
984985

985986
log_error_detail = technical_detail

0 commit comments

Comments
 (0)