Sep 19, 2026·5 min read·26 visits
Convoy versions prior to 26.6.8 allow low-privilege authenticated users or project-scoped API keys to bypass tenant isolation boundaries and extract unredacted message broker credentials of other projects by querying specific Source IDs.
CVE-2026-81505 is a high-severity Broken Object Level Authorization (BOLA) / Insecure Direct Object Reference (IDOR) vulnerability in Convoy, a cloud-native webhooks gateway. In affected versions prior to 26.6.8, the single-item Source retrieval API endpoint authorizes project access but fails to confirm if the requested Source belongs to that specific project. This logical flaw allows authenticated users or project-scoped API key holders to bypass tenant isolation boundaries and retrieve unredacted, plaintext message broker credentials for Apache Kafka, Amazon SQS, RabbitMQ, and Google Cloud Pub/Sub belonging to other tenants. This issue is fully patched in version 26.6.8.
Convoy serves as a high-performance, cloud-native webhooks gateway designed to manage, dispatch, and monitor webhook events. In multi-tenant deployments, isolation between logical projects is maintained through unique identifiers, where users or API keys are scoped strictly to specific projects. The primary attack surface resides in the control-plane REST APIs exposed for resource configuration and inspection.\n\nThe vulnerability designated as CVE-2026-81505 represents a classic Broken Object Level Authorization (BOLA) or Insecure Direct Object Reference (IDOR) flaw within the /api/v1/projects/{projectID}/sources/{sourceID} endpoint. While the API gateway validates the requester's authority over the {projectID} specified in the URL path, the application fails to enforce that the requested {sourceID} belongs to that authorized project.\n\nAn authenticated attacker with access to at least one valid project can leverage this logic gap to query and retrieve any webhook Source across the entire system. Because webhook Sources hold highly sensitive connection details, exploiting this vulnerability leads directly to the exposure of unredacted credentials for backend message brokers. This vulnerability is restricted to the single-item retrieval endpoint; the bulk list endpoint remains properly scoped.
The root cause of CVE-2026-81505 is located in the logical separation between the API routing layer's authorization check and the database retrieval layer. When a request is made to the endpoint GET /api/v1/projects/{projectID}/sources/{sourceID}, the system routes the request through an authentication and authorization middleware that validates the caller's rights to {projectID}.\n\nOnce authorization is confirmed, the router handler (Handler.GetSource) calls the backend service layer via sources.Service.FindSourceByID(ctx, projectID, sourceID). The primary design failure is that the underlying database query against the convoy.sources table is executed using only the primary key id (corresponding to {sourceID}). The query completely ignores the project_id context passed from the API route.\n\nBecause the database retrieval was not constrained by the project ID, and the application service layer did not implement a post-query validation check to compare the retrieved record's project_id with the client's authorized {projectID}, the service blindly returned the record. This lack of validation violates tenant isolation boundaries.
The vulnerability was resolved in commit 1cc67cd16fb1f8890cc83a3998d3f92dceb7fd06 by introducing an explicit tenant validation check in the FindSourceByID function located within internal/sources/impl.go.\n\ngo\n// FindSourceByID retrieves a source by its ID, scoped to the given project.\nfunc (s *Service) FindSourceByID(ctx context.Context, projectID, id string) (*datastore.Source, error) {\n\trow, err := s.repo.FetchSourceByID(ctx, common.StringToPgText(id))\n\tif err != nil {\n\t\tif errors.Is(err, pgx.ErrNoRows) {\n\t\t\treturn nil, datastore.ErrSourceNotFound\n\t\t}\n\t\treturn nil, &ServiceError{ErrMsg: "error retrieving source", Err: err}\n\t}\n\n-\treturn rowToSource(row)\n+ source, err := rowToSource(row)\n+ if err != nil {\n+ \treturn nil, err\n+ }\n+\n+ // FetchSourceByID keys only on the source id, so enforce the project scope\n+ // here (the sibling FindSourceByName/DeleteSourceByID scope in SQL). Without\n+ // this, any authorized caller could read another project's source by id,\n+ // including its plaintext broker credentials. Treat a cross-project hit as\n+ // not found so ids stay non-enumerable.\n+ if source.ProjectID != projectID {\n+ \treturn nil, datastore.ErrSourceNotFound\n+ }\n+\n+ return source, nil\n}\n\n\nBy adding the conditional statement if source.ProjectID != projectID, the service ensures that even if a database row is successfully retrieved using only the source identifier, the application rejects the request and returns a datastore.ErrSourceNotFound error if the source is owned by a different project. This prevents information disclosure and prevents ID enumeration attacks.
To exploit CVE-2026-81505, an attacker must first obtain a valid, authenticated session or an API key scoped to a project under their control (e.g., Project_A). The attacker also needs to identify or guess the UUID of a target webhook Source (e.g., source_B_uuid) belonging to another tenant's project (Project_B).\n\nThe attack flow is represented in the diagram below:\n\nmermaid\ngraph LR\n Attacker["Attacker\n(Auth for Project A)"]\n Gateway["Convoy API Gateway\n(Verifies Project A Access)"]\n ServiceLayer["Service Layer\n(Fetches Source B by ID)"]\n Database[("PostgreSQL\n(sources Table)")]\n\n Attacker -->|"GET /projects/Project_A/sources/Source_B"| Gateway\n Gateway -->|"FindSourceByID(Project_A, Source_B)"| ServiceLayer\n ServiceLayer -->|"SELECT * WHERE id = Source_B"| Database\n Database -->|"Returns Source B Record"| ServiceLayer\n ServiceLayer -->|"Leaks Source B Plaintext Credentials"| Attacker\n\n\nThe attacker crafts a direct HTTP request to the target Convoy instance, substituting the victim's source UUID in the place of the source ID, while keeping the attacker's own authorized project ID in the path parameters:\n\nhttp\nGET /api/v1/projects/Project_A_ID/sources/Project_B_Source_UUID HTTP/1.1\nHost: convoy.target.internal\nAuthorization: Bearer <Attacker_Project_A_API_Key>\nAccept: application/json\n\n\nBecause the routing layer only verifies authorization for Project_A_ID, the request is processed. The database retrieves the configuration details for Project_B_Source_UUID, which is returned in full, exposing raw, unredacted credentials.
The impact of exploiting CVE-2026-81505 is severe confidentiality compromise. Webhook sources are typically integrated with enterprise message brokers and streaming platforms. Consequently, their configuration records contain highly sensitive authentication credentials.\n\nAn attacker successful in exploiting this vulnerability can retrieve plaintext connection parameters, including Apache Kafka SASL usernames and passwords, Amazon Simple Queue Service (SQS) AWS IAM access keys and secret keys, RabbitMQ / AMQP passwords and URI strings, or Google Cloud Pub/Sub service account keys.\n\nExfiltration of these credentials grants the attacker direct access to downstream enterprise data streams, allowing them to read proprietary business events, inject unauthorized messages, or disrupt critical message queues, extending the impact far beyond the Convoy gateway itself.
The primary and recommended remediation is to upgrade all Convoy gateway installations to version v26.6.8 or later. This version enforces strict application-level validation on the retrieved source records to ensure they match the authorized project scope.\n\nIf upgrading cannot be performed immediately, temporary mitigation strategies include restricting project-scoped API key generation, limiting network-level access to the Convoy control plane, and auditing active API keys.\n\nAdditionally, security teams should implement centralized logging to monitor cross-tenant HTTP requests targeting the project endpoints. If exploitation is suspected or verified, immediately revoke and rotate all credentials associated with integrated message brokers.
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
convoy frain-dev | < 26.6.8 | 26.6.8 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-639 (Authorization Bypass Through User-Controlled Key) |
| Attack Vector | Network (AV:N) |
| CVSS v4.0 Score | 7.1 (High) |
| EPSS Score | Not Registered |
| Impact | Confidentiality: High (Plaintext credentials leak) |
| Exploit Status | None (No public PoCs) |
| KEV Status | Not Listed |
The system fails to prevent a user from accessing a resource by altering the direct object reference without verifying authorization for the specific object.
CVE-2026-62374 (GHSA-f9j6-8p6x-r9j6) is a verification bypass vulnerability in the OpenWallet Foundation SD JWT JS library (@sd-jwt/core and @sd-jwt/decode versions 0.19.0 and earlier). Due to permissive UTF-8 decoding and the omission of mandatory reference checks during presentation verification, tampered or unreferenced disclosures are silently dropped rather than rejected. This deviates from RFC 9901 §7.1 requirements and allows attackers to execute silent claim-set reduction attacks against relying parties.
An interpretation conflict in Bytebase DBHub (< 0.22.6) allows attackers to bypass read-only SQL query restrictions when connected to MySQL or MariaDB instances. Because DBHub's custom SQL tokenizer treats any '--' sequence as a comment, whereas MySQL and MariaDB require trailing whitespace or a control character, crafted queries containing '--' immediately followed by non-whitespace characters allow stacked destructive SQL statements to execute on connections configured as read-only.
CVE-2026-118777 is a prototype pollution vulnerability (CWE-1321) in the Forge JavaScript cryptography and networking library (distributed on npm as node-forge) prior to version 1.4.1. The flaw resides in the forge.http client subsystem, where incoming HTTP cookie names and paths parsed from Set-Cookie response headers are stored directly into standard JavaScript objects without key validation or property isolation. By returning crafted Set-Cookie headers containing keys such as __proto__ or constructor, a malicious HTTP server can inject arbitrary properties into Object.prototype, attach attributes to the global Object constructor, or delete built-in prototype methods to cause an application-wide denial of service.
CVE-2026-118775 (GHSA-4642-5mcj-2rqw) is an integer overflow and validation flaw in the ASN.1 serialization module of Forge (node-forge) prior to version 1.4.1. The asn1.oidToDer() function fails to validate the range of the first two arcs of an Object Identifier (OID) and truncates the packed initial subidentifier to a single 8-bit integer. Applications that perform policy checks on untrusted OID strings before encoding them into Distinguished Encoding Rules (DER) format can be tricked into emitting DER bytes that correspond to a completely different, restricted OID.
CVE-2026-119194 is an algorithmic complexity vulnerability (CWE-407) in the pyasn1 library for Python, affecting versions 0.3.3 through 0.6.4. The flaw resides in the schemaless decoding routine ConstructedPayloadDecoderBase._decodeComponentsSchemaless used across BER, CER, and DER codecs. By sending an ASN.1 container populated with child components whose tag identifiers are separated by multiples of CPython's integer hash modulus (sys.hash_info.modulus), an unauthenticated attacker can force worst-case hash collisions in an unbounded Python set, degrading decoding performance from O(N) to O(N^2) and causing a Denial of Service (DoS).
CVE-2026-119195 is an algorithmic complexity vulnerability (CWE-407) in the pyasn1 library for Python, affecting all versions prior to 0.6.5. When decoding constructed strings or indefinite-length ANY types across BER, CER, and certain DER code paths, the payload decoders repeatedly concatenate incoming fragments onto an immutable bytes object using the += operator. Because Python bytes objects cannot be resized in-place, each fragment iteration allocates a new buffer and copies all previously accumulated bytes, resulting in quadratic O(N^2) time and memory copy complexity. An unauthenticated remote attacker can supply a highly fragmented ASN.1 stream to exhaust CPU cycles and stall application worker threads.