JarValley

Market Prices

BTC Bitcoin
$82,046.1 +1.20%
ETH Ethereum
$2,403.8 +1.75%
SOL Solana
$104.74 +3.00%
BNB BNB Chain
$729.6 +2.66%
XRP XRP Ledger
$1.3 +2.68%
DOGE Dogecoin
$0.0794 +2.98%
ADA Cardano
$0.2174 +1.30%
AVAX Avalanche
$9.56 +1.57%
DOT Polkadot
$1.13 +6.60%
LINK Chainlink
$11.94 +2.01%

Event Calendar

{{年份}}
22
03
unlock Optimism Unlock

Circulating supply increases by about 2%

18
03
unlock Sui Token Unlock

Team and early investor shares released

28
03
unlock Arbitrum Token Unlock

92 million ARB released

15
04
halving Bitcoin Halving

Block reward reduced to 3.125 BTC

08
04
upgrade Solana Firedancer

Independent validator client goes live on mainnet

10
05
upgrade Ethereum Pectra Upgrade

Raises validator limit and account abstraction

12
05
halving BCH Halving

Block reward halving event

30
04
upgrade Celestia Mainnet Upgrade

Improves data availability sampling efficiency

Tools

All →

Altseason Index

40

Bitcoin Season

BTC Dominance Altseason

Market Cap

All →
# Coin Price
1
Bitcoin BTC
$82,046.1
1
Ethereum ETH
$2,403.8
1
Solana SOL
$104.74
1
BNB Chain BNB
$729.6
1
XRP Ledger XRP
$1.3
1
Dogecoin DOGE
$0.0794
1
Cardano ADA
$0.2174
1
Avalanche AVAX
$9.56
1
Polkadot DOT
$1.13
1
Chainlink LINK
$11.94

🐋 Whale Tracker

🔴
0x0f7a...11dd
12m ago
Out
31,658 SOL
🔵
0xd4e7...133b
12m ago
Stake
25,650 SOL
🔵
0x3697...e4b0
6h ago
Stake
3,858.77 BTC
In-depth

The Single Label That Broke a Trust Assumption: SAP's CVSS 10.0 SDK Flaw and the On-Chain Stack That Shares Its Shape

StackShark
The advisory landed on a Monday — October 13, SAP Security Patch Day — and the number printed at the top of it was 10.0. Not 9.8. Not 9.1 with a footnote about user interaction or scope. Ten point zero, the ceiling of the CVSS scale, the score the framework reserves for vulnerabilities that are remotely reachable, require no authentication, and end in total loss of confidentiality and integrity. It belonged to the SAP Cloud SDK for Python, a package most people building on-chain will never install, and it lived inside a single string parameter called tenant_subdomain. I read the advisory three times before I understood why it unsettled me. The reason was not the score. A 10.0 in an enterprise identity library is a bad week for SAP customers, but it is a bad week that ends with a patch and a rotation checklist. What unsettled me was the sentence structure of the thing. An application passes a subdomain. The SDK takes a token URL from configuration. The SDK replaces part of that URL with the value it was handed. The SDK sends a client ID and a client secret to the resulting address. Nothing in that chain verifies that the value means what the SDK assumes it means. That is the entire vulnerability. Tracked as CVE-2026-89111, classified CWE-918, server-side request forgery, scored 10.0. An attacker who controls the subdomain string controls where the credentials go, and because the request is the SDK's own outbound token call, it arrives at the attacker's listener already carrying the keys to the tenant's identity provider. No authentication needed. No user interaction. No exotic precondition beyond an application that passes a tenant subdomain it did not sanitize. I have audited enough governance code to recognise this shape from the inside. In 2017, at twenty-two, I spent six weeks inside a decentralized exchange protocol called EtherSwap while my peers were chasing token allocations. The bug I found was not a reentrancy. It was a voting path where whale wallets could bypass consensus because the contract trusted a balance assertion without verifying that the balance belonged to a distinct human. I published four thousand words about it and refused to buy the tokens, and the line I keep returning to from that piece is the one I still believe: code is law, but conscience is the compiler. The SAP flaw is the same sentence with different nouns. The SDK trusted that its modules carried the check. They did not. Why a crypto desk is reading an SAP advisory at all is the first thing worth explaining, because the reflex is to file this under enterprise identity and move on. The reflex is wrong. The SAP Cloud SDK for Python is a plumbing library. It exists so that developers building multi-tenant applications on SAP Business Technology Platform do not hand-roll OAuth2 client-credentials flows against every tenant they serve. You configure a token endpoint. You pass a tenant identifier. The SDK assembles the request, signs it with the client ID and client secret, and returns an access token. Multiply that pattern across thousands of applications and you have an identity substrate — invisible, unglamorous, load-bearing. Now hold that picture next to the stack most of us actually work in. Every wallet SDK that resolves an RPC endpoint from configuration. Every indexer that accepts a chain slug and builds a URL from it. Every oracle client that takes a feed address and queries a provider. Every cross-chain messaging library that accepts a destination identifier and constructs an attestation request. Every rollup sequencer configuration that points at a blob endpoint. In each case the library is doing exactly what the SAP SDK did: trusting that the string it received is the kind of string it assumed, and using that string to decide where secrets go. The architecture of multi-tenancy is what makes the SAP case sharp. In a single-tenant deployment, the token endpoint is a constant. In a multi-tenant deployment, the endpoint is a function of the tenant, which means the tenant identifier is an input to a security-critical decision, which means the tenant identifier is attack surface. SAP's design is not unusual here; it is the industry default. The SDK takes the token URL from the application's configuration, replaces a segment of it with the subdomain value, and sends the client credentials to the result. The subdomain is meant to be a label — a name. The SDK treated it as a name. The problem is that the SDK never enforced that it was one. An attacker who can influence that value does not need to breach SAP BTP. They do not need to compromise the tenant. They need only get a string into a parameter that an application passes through. Point it at a host they control and the SDK will dutifully POST the client ID and client secret to that host as part of a perfectly well-formed OAuth2 token request. The credentials are not encrypted beyond transport, and the attacker terminates the transport. What arrives is a working client secret for a multi-tenant application on SAP BTP — which, depending on the scopes attached, is the difference between reading one tenant's data and impersonating the application across many. This is the eleventh incident in a sequence that has been running since September 26, and the sequence itself is the story. The predecessors read like a roll call of identity infrastructure discovering that its defaults were doing work nobody had audited: masked-secret reuse in Keycloak, OAuth state reuse in Better Auth, SAML validation failure in Frontegg, patch regressions in Citrix SAML, spoofing CVEs in AD FS, OIDC invite hijacking in Budibase, a header bypass in OAuth2 Proxy, a dual-view XML gap in WorkOS SAML, credential abuse in ConnectWise Cloud Backup, plus two synthesis pieces and a retrospective on the wider OAuth and SAML bypass wave. Ten incidents, all of them scoring lower than 10.0, because all of them were misconfigured defaults, framework-level logic flaws, or credential abuse at the supply-chain layer. CVE-2026-89111 is the first to hit the ceiling, and it hits the ceiling because of where it lives. The previous ten lived in applications, in frameworks, in integrations. This one lives in the SDK — the foundational tooling that developers use to build applications on SAP BTP. A framework flaw compromises the applications built on it. A tooling flaw compromises everything anyone builds, forever, until they upgrade. The blast radius is not a deployment. It is a dependency graph. What makes the technical detail worth your time is the timeline, because the timeline is where the real lesson hides. The vulnerability existed from the SDK's first release on PyPI, on March 16, 2026. Version 0.45.2, released August 21, added a check. The check rejects any tenant subdomain that is not a simple name — a single label composed of letters, digits, and hyphens, up to sixty-three characters, per RFC 1123. That is precisely the right rule. It is narrow, it is testable, and it maps exactly to what a DNS label can be. A value that satisfies RFC 1123 single-label syntax cannot contain a dot, cannot contain a slash, cannot contain an at-sign, cannot contain a colon, and therefore cannot redirect a URL to another host. The check does not validate intent. It validates shape, and shape is sufficient here, because the attack requires a structural character that the rule forbids. The August release notes listed that fix under "Bug Fixes" without mentioning security. The formal advisory followed fifty-three days later, on October 13, as part of SAP Security Patch Day. Fifty-three days is not a scandal in enterprise disclosure terms; it is roughly normal, and in some vendor cultures it would be considered fast. But fifty-three days is an eternity for an operator who installed 0.45.2 on August 22, read "Bug Fixes," and moved on with their quarter. Release notes are a security control. When a validation check that closes a credential-exfiltration path is filed as a bug fix, the control is disabled by prose. The fix also did not hold across every code path, and this is the part I want to sit with, because it is the part that generalises. Testing published on October 13 found that six releases between version 0.53.0 and 0.55.0 fail the validation check when an application calls the SDK's Destination service module. Version 0.53.0 moved that module onto new shared code for requesting tokens, and the shared code did not include the check. Those six releases failed nine of the eleven tests that SAP added alongside version 0.55.1. Version 0.55.1, released September 18, restored the check to the shared code. Two older releases on the 0.36 line — 0.36.1 and 0.36.2 — also lack the check and fall inside the advisory's affected range. Version 0.58.4, the newest release on PyPI as of October 13, contains the check. Read that sequence as a story about code and it is a story about refactoring. Read it as a story about institutions and it is a story about how security invariants are stored. In August, the validation was added to three modules: Agent Gateway, Destination, and Document Management Service. Each module got its own copy of the check. That is a perfectly reasonable way to ship a fix quickly — touch the three call sites, add the tests, move on. Then in 0.53.0 the Destination module was migrated onto new shared token-request code. The module moved. The invariant did not move with it, because the invariant lived in the module rather than in the shared layer that the module now depended on. The check was not deleted. It was orphaned. The code that ran was not the code that had been reviewed. This is the same structural pattern the trust-through-defaults sequence has been documenting at the identity-layer level all year — a system trusting an assertion without verifying that the assertion means what the system assumes — now appearing inside the SDK's own codebase. The SDK trusted that its modules carried the check. They did not. The trust was reasonable. The trust was also, for six releases, false. And because the SDK is a library rather than a service, nobody was watching. There was no runtime anomaly. There was no alert. There was a green build and a passing test suite that did not include the eleven tests SAP would write later. What the regression reveals is that this class of vulnerability is not really about input validation. It is about invariant placement. A security check that lives at the leaves of a dependency tree is a check that can be lost by a refactor. A security check that lives at the root is a check that survives. The August fix placed the check at the leaves because the leaves were where the vulnerable call sites were. The correct fix, the one that shipped in 0.55.1, placed it in the shared code. Between August 21 and September 18, the library had the appearance of safety without its substance, and the appearance of safety is worse than no safety at all, because appearance is what lets an operator skip the credential rotation question. That question is still open. The advisory does not say whether applications that ran a release without the check should replace their client credentials. Read that sentence again, because it is the most important sentence in the entire disclosure. The advisory names the flaw, scores it, lists the affected versions, and offers a mitigation — validate the tenant subdomain against a known allowlist of registered tenants before passing it to the SDK. What it does not do is answer the only operational question that matters after a credential-exfiltration vulnerability: were my secrets exposed, and if I cannot prove they were not, when do I rotate? No exploitation beyond security testing has been confirmed. That is a statement about evidence, not about exposure. Absence of confirmed exploitation is not evidence of absence, and in a library that was downloadable from PyPI for five months with no check, the honest answer is that nobody can know. The allowlist mitigation deserves scrutiny of its own, because it is the pragmatic answer and pragmatism is where trust-through-defaults usually re-enters the building. Validating a tenant subdomain against a known allowlist of registered tenants is correct in effect: if the value must match a pre-registered name, the attacker cannot supply a hostname. But it moves the security decision from the library to the application, which means it moves the decision from code that is patched centrally to code that is written independently by every integrator. Some of those integrators will compare strings. Some will compare strings case-sensitively and break tenants whose registered names have mixed case. Some will build the allowlist from the same database that the untrusted input populates. The allowlist is not wrong. It is a compensating control, and compensating controls are, by definition, controls that compensate for the absence of the control you actually wanted. I have watched this exact migration happen in governance systems. At CivicChain in 2024 I designed a quadratic voting mechanism that weighted individual voices against capital weight, and we tested it with ten thousand simulated participants; participation from non-whale addresses rose forty percent. The design worked because the invariant — one identity, one weighting — lived in the tally layer rather than in each proposal's configuration. When we let individual proposals carry their own weighting rules, the invariant fragmented, and two proposals out of the pilot set produced tallies that were internally consistent and externally wrong. We fixed it by moving the rule back to the root. Governance is not a vote, it is a vigil, and the vigil is the part where you keep checking that the invariant is still where you left it. The crypto stack has a version of this problem that is worse, and I want to name it plainly, because the SAP advisory is a mirror and not a warning. Consider how oracle configuration works in practice. A protocol deploys, points at a feed, and trusts the feed's answer. The feed's own decentralisation story is told at the network layer — many nodes, many operators, a threshold of agreement — while the operational reality at the integration layer is a single address in a config file that somebody pasted from documentation. If that address is wrong, or if the endpoint that resolves it is wrong, the protocol does not know. It has no shape check for an oracle. It has no RFC 1123 equivalent that says this identifier cannot possibly mean something other than what I think it means. It trusts the assertion, and the assertion is a string. Consider cross-chain verification the same way. The libraries that move messages between chains accept a destination identifier and construct a verification path from it. The trust model is documented as decentralised verification, and the actual trust model is a set of configured entities — an oracle process, a relayer process — whose honesty is assumed rather than proven at the moment of the message. That gap between the documented trust model and the executed trust model is the same gap as the SAP SDK's. The documentation says the subdomain is a name. The code says the subdomain is a URL segment. Nobody reconciled the two until a CVSS 10.0 forced the reconciliation. And consider the rollup layer, where the shared-code problem is structural rather than accidental. Rollups inherit security from the settlement layer, and they inherit cost from the data availability layer, and those two inheritances are governed by different clocks. Blobspace is a shared resource with a fee market, and the current pricing regime is generous enough that rollups can treat it as cheap. That generosity is a parameter, not a law. When blob demand saturates — and the trajectory suggests it will, inside two years — the fee market does what fee markets do, and every rollup that inherited its economics from a shared layer discovers that the inheritance was conditional. The Destination module inherited the shared token code and lost the check. Rollups inherit shared blobspace and will lose the subsidy. The mechanism is identical: an invariant that was true at the layer above is assumed to be true at the layer below, and the assumption is never re-verified because the layer above is not where the code runs. This is why the SAP case is worth four thousand words rather than a paragraph. It is a clean, well-documented, fully-disclosed instance of a failure mode that the on-chain economy runs on top of and rarely audits. Enterprise identity has patch days, CVSS scores, advisories, and a vendor with a legal department. Crypto has upgrade timelocks, governance proposals, and the sincere hope that the multisig is paying attention. The SAP flaw was scored 10.0 and patched within weeks across a version range that is publicly enumerable. The equivalent flaw in a wallet SDK would be discovered by a researcher, disclosed on a Tuesday, argued about on a forum for three weeks, and fixed in a release that half the integrators would never adopt because upgrading breaks their build. Here is where I want to test the comfortable reading, because the comfortable reading is that SAP shipped a bad fix and then shipped a good one, and the system worked. The uncomfortable reading is that the bad fix was the more dangerous artifact, and it was dangerous precisely because it looked like a good one. A library with no check is a library that everyone knows is unsafe. A library with a check in three modules and not in the shared code is a library that passes review, satisfies the auditor, generates a release note, and quietly routes credentials to an attacker's host for six versions. The August fix did not reduce risk monotonically. It converted a known unknown into an unknown unknown, and unknown unknowns are the ones that survive incident response. There is a second uncomfortable reading, and it concerns the fifty-three days. The security community has spent a decade arguing about disclosure timelines, and the argument usually assumes that the vendor controls the clock. Here the vendor did control the clock, and used it to move from a silent bug fix to a formal advisory — a legitimate sequence, defensible in almost any framework. But the fifty-three days were not neutral for the operator. They were fifty-three days during which a person could reasonably believe that 0.45.2 was patched, run it in production, and be wrong, because the patch note said "Bug Fixes" and the advisory that would have said otherwise did not exist yet. Disclosure policy is usually framed as a tension between vendor reputation and attacker advantage. It is also, always, a tension between vendor reputation and operator accuracy, and operator accuracy loses every time. The pragmatism test, then, is not whether SAP responded well. On the evidence, SAP responded adequately: a check, tests, a restore, an advisory, a mitigation, a current release that contains the fix. The pragmatism test is whether the operator can now answer three questions without guessing. Which of my deployed applications call the Destination service module, and on which SDK version? Did any of them run a release between 0.53.0 and 0.55.0, or 0.36.1 and 0.36.2, during a window when an untrusted tenant subdomain could have reached them? And if the answer to the second question is yes or unknown, have I rotated the client secrets? Most operators can answer the first. Fewer can answer the second. Almost none will answer the third, because rotation is expensive, because the advisory did not instruct it, and because the absence of confirmed exploitation reads, to a busy team, like permission to do nothing. That is the blind spot, and it is not SAP's blind spot. It is the blind spot of every ecosystem that treats patching as a version-management exercise rather than a credential-hygiene exercise. A patch closes the path forward. It does not close the path backward. If a secret left the building between March 16 and September 18, the September patch is irrelevant to that secret, and the only control that matters is rotation. Silence in the bear market is where truth compiles, and the truth compiling here is that disclosure documents describe vulnerabilities while operators need to know about exposures, and those are different documents. I want to be careful not to over-claim. No exploitation beyond security testing has been confirmed, and that matters. It matters because rotation has real costs and because crying wolf about credential exposure is its own kind of harm. But the phrase "no exploitation confirmed" is doing a lot of work in this advisory, and the work it is doing is the work of ambiguity. It does not say the credentials were safe. It says nobody has produced evidence that they were not. In a library distributed from PyPI to an unknown population of applications, over five months, with an unauthenticated remote path to exfiltrate secrets, the epistemically honest posture is that exposure is possible and unquantified. Operators who can rotate should rotate. Operators who cannot should document why, in writing, with a date, so that the decision has an owner. What I keep coming back to is the shape of the fix rather than the shape of the bug. The bug is ordinary: a URL built from an untrusted string. There are ten thousand of these in the world and there will be ten thousand more. The fix is what is instructive. The right fix was a shape check — RFC 1123, single label, letters and digits and hyphens, sixty-three characters — and the right place for the fix was the shared code, and the right way to know the fix was in place was a test suite of eleven cases that fails loudly when a module migrates off the checked path. Three properties: narrow rule, root placement, adversarial tests. SAP got the first property right in August and the second and third right in September. The six-week window between those two states is where the risk lived. Every protocol I have worked inside has the same three-property problem and solves it worse. The narrow rule is usually absent, because the identifier that gets passed around is often a URL rather than a name, and once an identifier is a URL the shape check becomes a parser and the parser becomes the vulnerability. The root placement is usually absent, because the invariant lives in a per-module configuration and the configuration is set by governance proposals that are written by whoever shows up. The adversarial tests are usually absent, because the test suite tests the happy path and the happy path is what the documentation describes. We do not build walls, we weave nets of trust, and a net is only as strong as the assumption that every strand is knotted to the same rope. There is a specific way this plays out in DeFi that deserves naming, because it is the closest analogue to the SAP flaw and it is chronically under-audited. Oracle feed latency is the Achilles' heel of every lending market, and the reason is not that the feeds are slow. The reason is that the market trusts the feed's timestamp without verifying that the timestamp means what the market assumes. A price is not a number; a price is a number plus a moment plus a source, and the integration layer usually encodes only the number. When the source changes, or the moment drifts, or the endpoint that resolves the source is redirected, the market does not detect a shape violation because it never defined a shape. The SAP SDK at least knew what a tenant subdomain was supposed to look like. Most price integrations do not know what a valid price update looks like beyond "it parses as an integer." The chain of custody for a secret is the thing the advisory leaves undrawn. A client secret is created, stored in configuration, sent by an SDK to a URL derived from an untrusted string, and then — for six releases across an unknown population of applications — possibly sent somewhere else. The advisory documents the derivation. It does not document the custody. And custody is what governance is for. At GovernAI in 2025, when automated voting bots began manipulating proposal outcomes under the banner of efficiency, the coalition I helped organise did not argue about the bots' accuracy. We argued about custody: who holds the decision, and can that holding be audited. Fifteen community members, a Human-in-the-Loop charter, a fight against a board that wanted total automation, and a win that established the first industry standard for hybrid governance. The lesson transferred directly. Algorithmic efficiency cannot replace moral judgment, and a release note that says "Bug Fixes" cannot replace an operator's decision about whether to rotate a secret. So what do we owe the next release? Not a pledge to read release notes more carefully, because that pledge will be broken by the second Tuesday. What we owe is structural. Advisories should carry an explicit exposure statement in addition to a vulnerability statement — not "no exploitation confirmed," which is a fact about the world, but "credential rotation is required" or "credential rotation is not required," which is an instruction about the operator's next hour. Release notes should tag security-relevant entries, not to disclose early, but to ensure that the fifty-three days between a silent fix and a formal advisory are not fifty-three days of false confidence. And libraries should treat security invariants as root-level assets with migration tests attached, so that a module moving onto shared code cannot silently shed a check. None of that is exotic. All of it is governance, in the boring sense — the rules about how decisions get made and who is accountable when they are not. The on-chain economy is about to learn this lesson at scale, because the same structural inheritance problem is baked into how rollups borrow security and how bridges borrow verification and how wallets borrow endpoint resolution. In the chaos of summer, we found our winter soul, and the winter soul of this cycle is a set of shared libraries that nobody governs and everybody depends on. The SAP advisory is a gift: a clean case, fully documented, with a score high enough that people will read it. The question is whether we read it as a story about SAP or as a story about ourselves. My forward judgment is that CVE-2026-89111 will be cited for years, but not for the reason it was filed. It will be cited as the moment the trust-through-defaults sequence reached the tooling layer, and as the first clear demonstration that a security invariant is a governance object rather than a code object. Invariants have owners. Invariants have custody. Invariants can be orphaned by a refactor and nobody notices, because the build is green and the tests pass and the release notes say the bug is fixed. The score was 10.0 because the flaw was total. The lesson is 10.0 because the flaw was ordinary. Which of your dependencies is currently trusting a string it never validated, and who signed off on the rotation plan when it turns out the string went somewhere else?

The Single Label That Broke a Trust Assumption: SAP's CVSS 10.0 SDK Flaw and the On-Chain Stack That Shares Its Shape

The Single Label That Broke a Trust Assumption: SAP's CVSS 10.0 SDK Flaw and the On-Chain Stack That Shares Its Shape

The Single Label That Broke a Trust Assumption: SAP's CVSS 10.0 SDK Flaw and the On-Chain Stack That Shares Its Shape

Fear & Greed

59

Greed

Market Sentiment

Gas Tracker

Ethereum 28 Gwei
BNB Chain 3 Gwei
Polygon 42 Gwei
Arbitrum 0.5 Gwei
Optimism 0.3 Gwei

💡 Smart Money

0x9071...e53b
Market Maker
+$0.2M
62%
0xba82...fe66
Institutional Custody
+$0.8M
84%
0xcb66...a5f9
Early Investor
+$3.8M
82%