CVE-2026-40070: Bsv sdk Bsv-sdk And Bsv-wallet Persist Unverified Vulnerability
HIGHCVE-2026-40070 is a HIGH (CVSS 8.1) bsv-sdk and bsv-wallet persist unverified vulnerability (CWE-347: Improper Verification of Cryptographic Signature) affecting Bsv sdk libraries in the RubyGems ecosystem. The issue involves bsv-sdk and bsv-wallet persist unverified certifier signatures in acquire_certificate (direct and issuance paths). Remediation is available by upgrading affected packages to version 0.8.2, 0.3.4.
CVE-2026-40070 at a Glance
Security Impact
CVSS: 8.1
The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N).
How to Fix CVE-2026-40070
To remediate this vulnerability, upgrade all affected Bsv sdk dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.
Upgrade bsv-sdk to 0.8.2
Upgrade bsv-wallet to 0.3.4
Run the following command in your project directory to verify that all resolved dependencies use the patched release:
inspect dependencies for bsv-sdk
How to Check if You Are Affected
To determine whether your application is affected by CVE-2026-40070, inspect your project dependencies for Bsv sdk:
inspect dependencies for bsv-sdk
Analyze the terminal output to locate any version of Bsv sdk in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 0.8.2), your application is affected.
Note: Verify declared build manifests and deployed runtime container environments.
Technical Details
Vulnerability Mechanism & Technical Analysis
This vulnerability is classified under CWE-347: Improper Verification of Cryptographic Signature, where the product does not verify, or incorrectly verifies, the cryptographic signature for data. In Bsv sdk, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.
Source Advisory Statement
CVE-2026-40070 Affected Bsv sdk Versions
2 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
bsv-sdk
|
RubyGems |
≥ 0.3.1
< 0.8.2
|
0.8.2
|
|
bsv-wallet
|
RubyGems |
≥ 0.1.2
< 0.3.4
|
0.3.4
|
References
Frequently Asked Questions
What is CVE-2026-40070?
Which Bsv sdk versions are affected?
What version fixes CVE-2026-40070?
Is bsv-sdk affected?
Is bsv-wallet affected?
What is the CVSS score of CVE-2026-40070?
What CWE is associated with CVE-2026-40070?
How can I check RubyGems dependencies for CVE-2026-40070?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-hc36-c89j-5f4j",
"modified" : "2026-05-13T16:21:47Z",
"published" : "2026-04-09T20:28:10Z",
"aliases" : [ "CVE-2026-40070" ],
"summary" : "bsv-sdk and bsv-wallet persist unverified certifier signatures in acquire_certificate (direct and issuance paths)",
"details" : "# Unverified certifier signatures persisted by `acquire_certificate`\n\n## Affected packages\n\nBoth `bsv-sdk` and `bsv-wallet` are published from the [sgbett/bsv-ruby-sdk](https://github.com/sgbett/bsv-ruby-sdk) repository. The vulnerable code lives in `lib/bsv/wallet_interface/wallet_client.rb`, which is **physically shipped inside both gems** (the `bsv-wallet.gemspec` `files` list bundles the entire `lib/bsv/wallet_interface/` tree). Consumers of either gem are independently vulnerable; the two packages are versioned separately, so each has its own affected range.\n\n| Package | Affected | Patched |\n| --- | --- | --- |\n| `bsv-sdk` | `>= 0.3.1, < 0.8.2` | `0.8.2` |\n| `bsv-wallet` | `>= 0.1.2, < 0.3.4` | `0.3.4` |\n\n## Summary\n\n`BSV::Wallet::WalletClient#acquire_certificate` persists certificate records to storage **without verifying the certifier's signature** over the certificate contents. Both acquisition paths are affected:\n\n- `acquisition_protocol: 'direct'` — the caller supplies all certificate fields (including `signature:`) and the record is written to storage verbatim.\n- `acquisition_protocol: 'issuance'` — the client POSTs to a certifier URL and writes whatever signature the response body contains, also without verification.\n\nAn attacker who can reach either API (or who controls a certifier endpoint targeted by the issuance path) can forge identity certificates that subsequently appear authentic to `list_certificates` and `prove_certificate`.\n\n## Details\n\nBRC-52 requires a certificate's `signature` field to be verified against the claimed certifier's public key over a canonical hashing of `(type, subject, serialNumber, revocationOutpoint, fields)` before the certificate is trusted. The reference TypeScript SDK enforces this in `Certificate.verify()`.\n\n### Direct path\n\nThe Ruby implementation's `acquire_via_direct` path (`lib/bsv/wallet_interface/wallet_client.rb`) constructs the certificate record directly from caller-supplied fields:\n\n```ruby\ndef acquire_via_direct(args)\n {\n type: args[:type],\n subject: @key_deriver.identity_key,\n serial_number: args[:serial_number],\n certifier: args[:certifier],\n revocation_outpoint: args[:revocation_outpoint],\n signature: args[:signature],\n fields: args[:fields],\n keyring: args[:keyring_for_subject]\n }\nend\n```\n\nThe returned record is then written to the storage adapter by `acquire_certificate`. No verification of `args[:signature]` against `args[:certifier]`'s public key occurs at any point in this path.\n\n### Issuance path\n\n`acquire_via_issuance` POSTs to a certifier-supplied URL and parses the response body into a certificate record, which is then written to storage without verifying the returned signature. A hostile or compromised certifier endpoint — or anyone able to redirect/MITM the plain HTTP request — can therefore return an arbitrary `signature` value for any subject and have it stored as authentic. This is the same class of bypass as the direct path; it was tracked separately as finding **F8.16** in the compliance review and is closed by the same fix.\n\n### Downstream impact\n\nDownstream reads via `list_certificates` and selective-disclosure via `prove_certificate` treat stored records as valid without re-verifying, so any forgery that slips past `acquire_certificate` is trusted permanently.\n\n## Impact\n\nAny caller that can invoke `acquire_certificate` — via either acquisition protocol — can forge a certificate attributed to an arbitrary certifier identity key, containing arbitrary fields, and have it persisted as authentic. Applications and downstream gems that rely on the wallet's certificate store as a source of truth for identity attributes (e.g. KYC assertions, role claims, attestations) are subject to credential forgery.\n\nThis is a credential-forgery primitive, not merely a spec divergence from BRC-52.\n\n## CVSS rationale\n\n`AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N` → **8.1 (High)**\n\n- **AV:N** — network-reachable in any wallet context that exposes `acquire_certificate` to callers.\n- **AC:L** — low attack complexity: pass arbitrary bytes as `signature:`.\n- **PR:L** — low privileges: any caller authorised to invoke `acquire_certificate`.\n- **UI:N** — no user interaction required.\n- **C:H** — forged credentials via `prove_certificate` can assert attributes about the subject.\n- **I:H** — the wallet's credential store is polluted with attacker-controlled data.\n- **A:N** — availability unaffected.\n\n## Proof of concept\n\n```ruby\nclient = BSV::Wallet::WalletClient.new(key, storage: BSV::Wallet::MemoryStore.new)\n\nclient.acquire_certificate(\n type: 'age-over-18',\n acquisition_protocol: 'direct',\n certifier: claimed_trusted_pubkey_hex,\n serial_number: 'any-serial',\n revocation_outpoint: ('00' * 32) + '.0',\n signature: 'deadbeef' * 16, # arbitrary bytes — never verified\n fields: { 'verified' => 'true' },\n keyring_for_subject: {}\n)\n\nclient.list_certificates(\n certifiers: [claimed_trusted_pubkey_hex],\n types: ['age-over-18']\n)\n# => returns the forged record as if it were a real certificate from that certifier\n```\n\n## Affected versions\n\nThe vulnerable direct-path code was introduced in commit `d14dd19` (\"feat(wallet): implement BRC-100 identity certificate methods (Phase 5)\") on 2026-03-27 20:35 UTC. The vulnerable issuance-path code was added one day later in `6a4d898` (\"feat(wallet): implement certificate issuance protocol\", 2026-03-28 04:38 UTC), which removed an earlier `raise UnsupportedActionError` and replaced it with an unverified HTTP POST.\n\n**`bsv-sdk`:** the v0.3.1 chore bump (`89de3a2`) was committed 28 minutes after `d14dd19`, so the direct-path bypass shipped in the **v0.3.1** tag. The v0.3.1 release raised `UnsupportedActionError` for the issuance path, so the issuance-path bypass first shipped in **v0.3.2** (`5a335de`). Every subsequent release up to and including **v0.8.1** is affected by at least one path, and every release from v0.3.2 onwards is affected by both. Combined affected range: `>= 0.3.1, < 0.8.2`.\n\n**`bsv-wallet`:** at the time both commits landed, the wallet gem was at version 0.1.1. The first wallet release containing any of the vulnerable code was **v0.1.2** (`5a335de`, 2026-03-30), which shipped both paths simultaneously. Every subsequent release up to and including **v0.3.3** is affected on both paths. Affected range: `>= 0.1.2, < 0.3.4`.\n\n## Patches\n\nUpgrade to `bsv-sdk >= 0.8.2` **and/or** `bsv-wallet >= 0.3.4`. Both releases ship the same fix: a new module `BSV::Wallet::CertificateSignature` (`lib/bsv/wallet_interface/certificate_signature.rb`), which builds the BRC-52 canonical preimage (`type`, `serial_number`, `subject`, `certifier`, `revocation_outpoint`, lexicographically-sorted `fields`) and verifies the certifier's signature against it via `ProtoWallet#verify_signature` with protocol ID `[2, 'certificate signature']` and counterparty = the claimed certifier's public key. Both `acquire_via_direct` and `acquire_via_issuance` now call `CertificateSignature.verify!` before returning the certificate to `acquire_certificate`, so invalid certificates raise `BSV::Wallet::CertificateSignature::InvalidError` (a subclass of `InvalidSignatureError`) and are never written to storage.\n\nConsumers should upgrade whichever gem they depend on directly; they do not need both. `bsv-wallet 0.3.4` additionally tightens its dependency on `bsv-sdk` from the stale `~> 0.4` to `>= 0.8.2, < 1.0`, which forces the known-good pairing and pulls in the sibling advisory fixes (F1.3, F5.13) tracked separately.\n\nThe issuance-path fix also partially closes finding **F8.16** from the same compliance review. F8.16's second aspect — switching the issuance transport from ad-hoc JSON POST to BRC-104 AuthFetch — is not addressed here and remains deferred to a future release.\n\nFixed in sgbett/bsv-ruby-sdk#306.\n\n## Workarounds\n\nIf upgrading is not immediately possible:\n\n- Do not expose `acquire_certificate` (either acquisition protocol) to untrusted callers.\n- Do not invoke `acquire_certificate` with `acquisition_protocol: 'issuance'` against a certifier URL you do not fully trust, and require TLS for any such request.\n- Treat any record returned by `list_certificates` / `prove_certificate` as unverified and perform an out-of-band BRC-52 verification against the certifier's public key before acting on it.\n\n## Credit\n\nIdentified during the 2026-04-08 cross-SDK compliance review, tracked as findings F8.15 (direct path) and F8.16 (issuance path, partial).\n\n## References\n\n- HLR: sgbett/bsv-ruby-sdk#305\n- Fix PR: sgbett/bsv-ruby-sdk#306\n- Compliance review: [`.architecture/reviews/20260408-cross-sdk-compliance-review.md`](../../.architecture/reviews/20260408-cross-sdk-compliance-review.md)\n- BRC-52 specification: https://brc.dev/52\n- TypeScript reference: `Certificate.verify()` in `@bsv/sdk`",
"severity" : [ {
"type" : "CVSS_V3",
"score" : "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N"
} ],
"affected" : [ {
"package" : {
"ecosystem" : "RubyGems",
"name" : "bsv-sdk"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "0.3.1"
}, {
"fixed" : "0.8.2"
} ]
} ]
}, {
"package" : {
"ecosystem" : "RubyGems",
"name" : "bsv-wallet"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "0.1.2"
}, {
"fixed" : "0.3.4"
} ]
} ]
} ],
"references" : [ {
"type" : "WEB",
"url" : "https://github.com/sgbett/bsv-ruby-sdk/security/advisories/GHSA-hc36-c89j-5f4j"
}, {
"type" : "ADVISORY",
"url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-40070"
}, {
"type" : "WEB",
"url" : "https://github.com/sgbett/bsv-ruby-sdk/issues/305"
}, {
"type" : "WEB",
"url" : "https://github.com/sgbett/bsv-ruby-sdk/pull/306"
}, {
"type" : "WEB",
"url" : "https://github.com/sgbett/bsv-ruby-sdk/commit/4992e8a265fd914a7eeb0405c69d1ff0122a84cc"
}, {
"type" : "WEB",
"url" : "https://brc.dev/52"
}, {
"type" : "WEB",
"url" : "https://github.com/rubysec/ruby-advisory-db/blob/master/gems/bsv-sdk/CVE-2026-40070.yml"
}, {
"type" : "WEB",
"url" : "https://github.com/rubysec/ruby-advisory-db/blob/master/gems/bsv-wallet/CVE-2026-40070.yml"
}, {
"type" : "PACKAGE",
"url" : "https://github.com/sgbett/bsv-ruby-sdk"
} ],
"database_specific" : {
"cwe_ids" : [ "CWE-347" ],
"severity" : "HIGH",
"github_reviewed" : true,
"github_reviewed_at" : "2026-04-09T20:28:10Z",
"nvd_published_at" : "2026-04-09T18:17:03Z"
}
}