Testing the relatively new function mocking feature of OPA revealed a vulnerability in the Go API, where the use of the WithUnsafeBuiltins function on the compiler object — a deprecated legacy function used to declare a set of function names as unsafe, and as such rejected in the policy compilation stage — could be bypassed by mocking a function, effectively replacing it with one of the functions deemed unsafe.
The same function is exposed via the higher-level rego.UnsafeBuiltins API in the github.com/open-policy-agent/opa/rego package.
This post outlines the status, impact and solution to the problem.
Users are affected by this vulnerability only when all of the below conditions are met:
- Use OPA from version v0.40.0 to (and including) v0.43.0.
- Allow policy evaluation of policies provided by untrusted parties.
- Use the Go API for policy evaluation (not the OPA server, or the Go SDK).
- Make use of the WithUnsafeBuiltins method in order to deny certain built-in functions, like e.g. http.send, from being used in policy evaluation.
- The policies evaluated include the with keyword to rewrite/mock a built-in, or custom, function to that of another built-in function, such as http.send.
Additionally, the OPA Query API is affected:
- If the OPA Query API is exposed to the public, and it is relied on http.send to be unavailable in that context. Exposing the OPA API to the public without proper authentication and authorization in place is generally advised against.
Although marked as deprecated since the introduction of the capabilities feature which was introduced in OPA v0.23.0, users of the Go API might not have seen an immediate reason to switch from the older WithUnsafeBuiltins function before. By unlucky coincidence, the function was marked as deprecated in a comment, but not in a way that followed convention for deprecated functions, and as such might not have been registered by editors, or other tooling.
Styra Declarative Authorization Service (DAS) customers are not directly affected by the vulnerability, as the WithUnsafeBuiltins function isn’t used anywhere in the product. Customers who have built their own advanced integrations using the OPA Go API and relied on WithUnsafeBuiltins as a protective measure are however recommended to upgrade their OPA dependency, and/or switch to using the capabilities feature. Please contact your CSM or email@example.com if you need help with this, or have further questions.
Although few users are expected to be affected, the impact for those who are could be quite severe. OPA does not come with many built-in functions that would be considered unsafe, but the http.send function would normally be the exception.
Since HTTP requests sent via http.send are sent from the context of the OPA instance, requests could be routed to services only exposed internally, like e.g. the AWS API, in order to extract sensitive information.
Although the opa.runtime built-in function seemingly would be another function to consider unsafe — given how it could be used to extract information from the environment (configuration data, and environment variables) — this function does not return any data when used via the Go API unless the developer actively provided that. It is highly unlikely that a developer chose to do so, while also marking the function as unsafe, and by doing so effectively disabling it from use.
Example policy demonstrating the bypass of WithUnsafeBuiltins using the with keyword for function mocking:
Users relying on the WithUnsafeBuiltins function of the OPA Go API are recommended to upgrade to either OPA v0.43.1 or v0.44.0. If an immediate upgrade is not possible, change the code to use the capabilities feature.
If you have any questions, feel free to reach out to firstname.lastname@example.org at any time!