security(registry): enforce configured search limits server-side in search_registrants#335
Draft
gonzalesedwin1123 wants to merge 6 commits into
Draft
security(registry): enforce configured search limits server-side in search_registrants#335gonzalesedwin1123 wants to merge 6 commits into
gonzalesedwin1123 wants to merge 6 commits into
Conversation
…passes The Registry Search controls (min_chars, result_limit, targeted mode) were enforced only in the JS client. Add regression tests (currently failing) pinning server-side enforcement in the search_registrants RPC: short and wildcard-only terms, caller limit above the configured maximum, targeted mode falling back to unified when search_field is missing/invalid, and TypeError crashes on malformed limit/search_term.
…earch_registrants The admin-configurable Registry Search controls were applied only in the JavaScript portal; a direct RPC call could search registrant PII (name, ID number, phone, email) with a one-character or wildcard-only term despite spp_registry_search.min_chars, request up to 200 results regardless of the configured maximum, and downgrade targeted mode to unified search by omitting search_field. Enforce everything at the RPC entry point: - min_chars counted on effective (non-wildcard) characters, so '%%%' no longer matches every registrant; matching semantics are unchanged - caller limit capped at the configured result_limit (callers may request fewer results, never more), mirroring get_search_config's clamps - targeted mode never falls back to unified: a missing or unknown search_field uses the configured target_field (valid caller-supplied fields stay honored, matching the portal UX) - malformed RPC inputs (non-string term, non-numeric limit) are rejected or coerced instead of raising TypeError as a generic 500 The JS client already complies with all of these, so no client change.
…rch limits Patch bump 19.0.2.1.1 -> 19.0.2.1.2. README.rst and static/description/index.html to be regenerated from CI's pinned generator.
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## 19.0 #335 +/- ##
==========================================
- Coverage 74.28% 68.13% -6.16%
==========================================
Files 372 144 -228
Lines 25385 13483 -11902
==========================================
- Hits 18857 9186 -9671
+ Misses 6528 4297 -2231
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
Generated files applied verbatim from CI's pinned oca-gen-addon-readme output, matching the HISTORY.md fragment.
…in target_field From staff review: a non-dict advanced_filters value raised AttributeError -> generic 500 (currently failing test), and the fail-closed behavior of targeted mode with an invalid admin-configured target_field was verified but untested.
A malformed advanced_filters RPC value (string, list, int) reached .get() and raised AttributeError -> unhandled 500, the same malformed-input class this fix closes for search_term and limit. Guard with isinstance so the filter block is skipped for non-dict values.
Member
Author
|
Staff-review follow-ups filed (pre-existing, out of this PR's scope):
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
spp_registry_search/models/res_partner.py::search_registrants(@api.model, callable by anyauthenticated user via
/web/dataset/call_kw) searches sensitive registrant PII — name, ID number,phone, email. The administrator-configurable Registry Search controls added with this feature were
enforced only in the JavaScript portal client; the RPC itself applied none of them:
min_charsbypass. The server's only term check wasif not search_term. A direct call witha one-character term ran
ilikeacross all PII fields. Worse:ilikedoes not escape SQL LIKEwildcards, so
search_term="%%%"(three characters — passing even a naive length check) matchedevery registrant.
limit = max(10, min(200, limit or config_limit))let the caller'slimitoverride the configuredspp_registry_search.result_limit, clamped only to thehardcoded [10, 200] — an admin cap of 20 was bypassed with
limit=200.if search_mode == "targeted" and search_field:fell back tounified search over all PII fields whenever
search_fieldwas omitted (or invalid), silentlyundoing the admin's targeted-mode restriction.
Adjacent weaknesses in the same entry point (included, same class as #325): a non-numeric
limit(e.g.
"abc") raisedTypeError→ unhandled generic 500, as did a non-stringsearch_term; andthe server did not mirror
get_search_config()'s [10, 200] / [1, 10] clamps.Not an ACL bypass — all searches run un-sudo'd, so ACLs and record rules still apply. The issue is
that the search-governance/anti-enumeration controls were client-side only. Severity: medium
(authenticated PII enumeration aid + throttling bypass).
Fix
Enforce everything server-side at the RPC entry point (single choke point; the JS client already
complies, so no client change):
search_term→[];limitcoerced viaint()with fallbackto the configured limit (no more TypeError → 500).
%,_) are stripped before counting,so
%%%andAl%are rejected undermin_chars=3. Matching semantics are deliberatelyunchanged — terms containing
_(e.g. emails) behave exactly as before.limit = min(limit, config_limit) if limit > 0 else config_limit,with
config_limit/min_charsclamped exactly asget_search_config()clamps them for the JS.Callers may request fewer results than the maximum, never more.
search_fieldfalls back to the configuredtarget_field; valid caller-supplied fields stay honored (the settings help text documents thatportal users may change the field).
Tests
Written test-first (red → green): the module had no tests at all for
search_registrants.New
tests/test_search_registrants.py(14 tests, 10 failing before the fix):%%%), wildcard-padded term (Al%) →[](all matched registrants before).
limit=200vs configured 10 → ≤10 (returned 12 before); lower caller limithonored; non-numeric limit no longer crashes (raised
TypeError: '<' not supported between instances of 'str' and 'int'before).search_fieldstays targeted (found the registrant via phone in unifiedfallback before); invalid field uses the configured default; valid caller field still honored.
search_typefilters, non-string term rejected../spp t spp_registry_search→ 0 failed, 0 error(s) of 36 tests (was 7 failed + 2 errors).Ruff/ruff-format clean. No existing tests removed or modified.
Staff-review round
An adversarial staff review confirmed the core fix (limit logic simulated against bool/float/negative/
huge/garbage inputs, targeted mode traced fail-closed, no JS regression) and caught one gap, fixed here:
a non-dict
advanced_filtersvalue reached.get()-> AttributeError -> unhandled 500 (the samemalformed-input class this PR closes). Guarded with
isinstance, plus a test; also added a test pinningthat targeted mode fails closed when the admin-configured
target_fieldis itself invalid.Scope
spp_registry_search.19.0.2.1.1 → 19.0.2.1.2+ HISTORY fragment;README.rst/static/description/index.htmlto be regenerated from CI's pinned generator.