diff --git a/spp_registry_search/README.rst b/spp_registry_search/README.rst index 0a19b8f1e..421f00e2c 100644 --- a/spp_registry_search/README.rst +++ b/spp_registry_search/README.rst @@ -124,6 +124,18 @@ Dependencies Changelog ========= +19.0.2.1.2 +~~~~~~~~~~ + +- fix(security): enforce the configured Registry Search controls + server-side in the ``search_registrants`` RPC method. The + minimum-character requirement (counting effective, non-wildcard + characters), the administrator's maximum result limit, and the + targeted search mode (no fallback to unified search when the field is + missing or invalid) are now applied on the server instead of only in + the JavaScript client, and malformed RPC inputs are rejected instead + of raising errors. + 19.0.2.1.1 ~~~~~~~~~~ diff --git a/spp_registry_search/__manifest__.py b/spp_registry_search/__manifest__.py index a3d026b85..8925d95b1 100644 --- a/spp_registry_search/__manifest__.py +++ b/spp_registry_search/__manifest__.py @@ -2,7 +2,7 @@ { "name": "OpenSPP Registry Search Portal", "category": "OpenSPP/Registry", - "version": "19.0.2.1.1", + "version": "19.0.2.1.2", "sequence": 1, "author": "OpenSPP.org", "website": "https://github.com/OpenSPP/OpenSPP2", diff --git a/spp_registry_search/models/res_partner.py b/spp_registry_search/models/res_partner.py index a11f4303a..70ad184bd 100644 --- a/spp_registry_search/models/res_partner.py +++ b/spp_registry_search/models/res_partner.py @@ -91,18 +91,38 @@ def search_registrants(self, search_term, search_type="all", search_field=None, Returns: list: List of dicts with partner data """ - if not search_term: + # Validate RPC inputs: this method is callable directly (bypassing the + # JS portal), so malformed values must not crash into a generic 500. + if not isinstance(search_term, str) or not search_term: return [] + try: + limit = int(limit) if limit else 0 + except (TypeError, ValueError): + limit = 0 - # Read config with fallback defaults + # Read config with fallback defaults, mirroring the clamps that + # get_search_config() applies for the JS client. get_param = self.env["ir.config_parameter"].sudo().get_param # nosemgrep: odoo-sudo-without-context - config_limit = int(get_param("spp_registry_search.result_limit", "50")) - limit = max(10, min(200, limit or config_limit)) + config_limit = max(10, min(200, int(get_param("spp_registry_search.result_limit", "50")))) + min_chars = max(1, min(10, int(get_param("spp_registry_search.min_chars", "3")))) + + # Enforce the configured minimum on *effective* characters: SQL LIKE + # wildcards (%, _) are stripped before counting, otherwise a term like + # '%%%' passes a plain length check yet matches every registrant. + if len(search_term.replace("%", "").replace("_", "").strip()) < min_chars: + return [] + + # Callers may request fewer results than the configured maximum, + # never more. + limit = min(limit, config_limit) if limit > 0 else config_limit search_mode = get_param("spp_registry_search.search_mode", "unified") - # If targeted mode and a field is specified, only search that field - if search_mode == "targeted" and search_field: + if search_mode == "targeted": + # Targeted mode never widens to unified search: a missing or + # unknown field falls back to the configured default field. + if search_field not in ("name", "id_number", "phone", "email"): + search_field = get_param("spp_registry_search.target_field", "name") partner_ids = self._search_by_field(search_term, search_field, limit) else: # Unified mode: run separate queries per field and merge @@ -119,7 +139,7 @@ def search_registrants(self, search_term, search_type="all", search_field=None, elif search_type == "groups": domain.append(("is_group", "=", True)) - if advanced_filters: + if advanced_filters and isinstance(advanced_filters, dict): if advanced_filters.get("registrationDateFrom"): domain.append(("registration_date", ">=", advanced_filters["registrationDateFrom"])) if advanced_filters.get("registrationDateTo"): diff --git a/spp_registry_search/readme/HISTORY.md b/spp_registry_search/readme/HISTORY.md index 1de70f102..d4fb6e3d9 100644 --- a/spp_registry_search/readme/HISTORY.md +++ b/spp_registry_search/readme/HISTORY.md @@ -1,3 +1,12 @@ +### 19.0.2.1.2 + +- fix(security): enforce the configured Registry Search controls server-side in the + `search_registrants` RPC method. The minimum-character requirement (counting effective, + non-wildcard characters), the administrator's maximum result limit, and the targeted + search mode (no fallback to unified search when the field is missing or invalid) are now + applied on the server instead of only in the JavaScript client, and malformed RPC inputs + are rejected instead of raising errors. + ### 19.0.2.1.1 - fix(security): gate the Registry Search "New Individual/Group" buttons on the registry create-permission roles instead of generic `res.partner` create access, so roles without registrant-create rights (e.g. validators) can no longer initiate creation (#1124) diff --git a/spp_registry_search/static/description/index.html b/spp_registry_search/static/description/index.html index 25e1fcec2..8758867b0 100644 --- a/spp_registry_search/static/description/index.html +++ b/spp_registry_search/static/description/index.html @@ -497,6 +497,19 @@

Changelog

+

19.0.2.1.2

+ +
+

19.0.2.1.1

-
+

19.0.2.0.0