Add @requires_view decorator and enforce permission checks for Thrift methods#4854
Open
bishara74 wants to merge 3 commits into
Open
Add @requires_view decorator and enforce permission checks for Thrift methods#4854bishara74 wants to merge 3 commits into
bishara74 wants to merge 3 commits into
Conversation
Introduces a @requires_view decorator in api/common.py that wraps a Thrift API method's view-permission check into the method's decorator chain, so the permission requirement is visible in the method's signature rather than buried in the first line of the body. The decorator looks up the handler's existing __require_view() helper via getattr on the name-mangled attribute, so the helper does not need to be renamed and the change is purely additive in report_server.py. This commit applies the decorator to ThriftRequestHandler.getRunData as a design proof. A follow-up commit will migrate the remaining self.__require_view() call sites in report_server.py to use the decorator.
Migrates the remaining 42 Thrift API method bodies in ThriftRequestHandler from starting with a literal self.__require_view() call to using the @requires_view decorator introduced in the prior commit. The decorator inserts the permission check before the method body executes, so behavior is unchanged but the permission requirement is now visible at the method's signature. Performed mechanically: each call site was located, the enclosing def was identified, @requires_view was inserted above the def at matching indentation, and the call line was removed. Net diff is symmetric (42 insertions, 42 deletions). All 31 server unit tests still pass.
Adds a static check that every method declared in the Thrift service
codeCheckerDBAccess_v6 has a detectable permission check on the
implementing ThriftRequestHandler. A method is considered protected if
any of the following holds:
* It has the @requires_view decorator (introduced earlier in this
branch).
* One of its first few statements calls a self.__require_*() or
self._require_*() permission helper (admin, access, store,
permission, etc.).
* It is listed in _DELEGATED_PROTECTION, in which case the check
verifies that the named delegate helper itself satisfies one of
the above conditions. This handles methods such as massStoreRun
that pass the request through __massStoreRun_common, which in
turn calls self.__require_store().
The check is wired into two places, both using the same logic so they
cannot disagree:
* As a unit test (test_thrift_permission_coverage.py), so a PR that
introduces an unprotected method fails CI before being merged.
* As a startup assertion in start_server(), so even if such a PR
landed in master, the server would refuse to start instead of
serving an exploitable endpoint.
Thrift method names are discovered by introspecting the generated
Iface class in codechecker_api.codeCheckerDBAccess_v6, which is
present in both development and deployed installations -- no
dependency on the .thrift source files.
The __require_view helper on ThriftRequestHandler is renamed to
_require_view (single underscore). The @requires_view decorator now
calls self._require_view() directly. Pylint's unused-private-member
checker cannot see through the previous getattr indirection that
worked around Python's name mangling, so the original double-
underscore form was flagged as dead code despite being live.
247a23c to
d824b7b
Compare
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.
Summary
Each Thrift API method in
ThriftRequestHandlerpreviously started with a literalself.__require_view()call to enforce its permission requirement. Nothing prevented a new method from being added without that call, which would expose it to unprivileged users.This PR introduces a
@requires_viewdecorator that hoists the permission check into the method's signature, migrates the existing call sites, and adds a static enforcement mechanism that makes it impossible for an unprotected Thrift method to slip past review or boot the server.Changes
api/common: add requires_view decorator for Thrift methodsIntroduces
@requires_viewinapi/common.py, alongside the existing@exc_to_thrift_reqfail. The decorator looks up the handler's__require_view()helper viagetattron the name-mangled attribute, so the underlying helper does not need to be renamed and the change toreport_server.pyis purely additive. Applied togetRunDatain this commit as a design proof.report_server: apply @requires_view to remaining Thrift methodsMigrates the remaining 42 Thrift methods in
ThriftRequestHandlerfrom starting withself.__require_view()to using the decorator. Done mechanically; net diff is symmetric (42 insertions, 42 deletions).api: enforce permission check on every Thrift API methodAdds
permission_coverage.py, a static check that every method declared in the Thrift servicecodeCheckerDBAccess_v6has a detectable permission check on the implementing handler class. A method is considered protected if any of:@requires_viewdecorator.self.__require_*()permission helper (__require_admin,__require_access,__require_store,__require_permission, etc.)._DELEGATED_PROTECTION, in which case the check verifies that the named delegate helper itself performs a permission check. This handlesmassStoreRunandmassStoreRunAsynchronous, both of which pass the request through__massStoreRun_common— which callsself.__require_store().The check runs in two places using the same code path, so they cannot disagree:
test_thrift_permission_coverage.py), so a PR that introduces an unprotected Thrift method fails CI before being merged.start_server(), so even if such a PR landed inmaster, the server refuses to start rather than serve an exploitable endpoint.Thrift method names are discovered by introspecting the generated
Ifaceclass incodechecker_api.codeCheckerDBAccess_v6, which ships with both development and deployed installations — no dependency on the.thriftsource files at runtime.Verification
Beyond the unit test (32 passing, was 31), the following was verified manually:
__require_view()before the wrapped method and propagates its return value (smoke-tested against a stand-in handler class).assert_all_thrift_methods_protected()returns silently against the realreport_server.py.RuntimeErrorwhen@requires_viewis removed from a Thrift method (simulated regression).permission_coverage.pycauses startup to fail with a traceback ending in theRuntimeError, before the listener is bound. Confirms the startup hook is effective and prevents an exploitable instance from running.permission_coverage.py, the server boots normally and serves Thrift requests for decorated methods such asgetRunData.Scope notes
__require_viewonly. The other permission helpers (__require_admin,__require_access,__require_store,__require_permission, plus the variants inproduct_server.py/authentication.py) remain as plain method calls. The enforcement check accepts either style, so security coverage is complete today; extending the decorator pattern to the other permissions can be done in a follow-up if preferred.codeCheckerDBAccess_v6is covered by the enforcement check. Extending to the other Thrift services (products,authentication,configuration,tasks,server_info) is a mechanical addition once the design here is approved.