Problem
Every line of code that builds, signs, and submits a transaction is fetched from a third-party CDN
at page load, over a URL with no integrity check:
frontend/soroban.js:3 -- const SDK_URL = "https://esm.sh/@stellar/stellar-sdk@14.5.0?bundle",
loaded via import(SDK_URL) on line 9
frontend/wallet.js:14 -- import("https://esm.sh/@stellar/freighter-api@5.0.0?bundle")
Neither is pinned by hash. A dynamic import() of a remote URL cannot carry a Subresource
Integrity attribute, so there is no mechanism verifying that the bytes esm.sh returns are the bytes
the package published.
And nothing constrains where scripts may be loaded from. netlify.toml sets
X-Content-Type-Options, Referrer-Policy, and Permissions-Policy, but no
Content-Security-Policy -- confirmed against the live response headers from
https://stellar-pulse.netlify.app/. There is no script-src, no connect-src, no
default-src.
Impact
stellar-sdk is the module that constructs the transaction, and freighter-api is the module that
hands it to the wallet for signature. If esm.sh is compromised, or its DNS or BGP is hijacked, or
its edge cache is poisoned for this origin, an attacker controls both. They can:
- rewrite the operation before it is built, so the XDR the user is asked to approve pays their
address instead of the market contract
- swap the
place_bet invocation for a transfer of the wallet's entire XLM balance
- intercept the signed XDR and submit a different transaction
- exfiltrate every connected address
The user's protection at that point is reading raw XDR in the Freighter confirmation dialog, which
in practice nobody does. This drains any wallet that connects to the site, and it requires no
vulnerability in this repo's own code -- only in a dependency delivery path this repo has chosen not
to control.
The exposure is not theoretical for a CDN of this kind: unpinned, unverified runtime module loading
from a public transpiling CDN is the exact shape of the polyfill.io class of incident.
What to do
Why this is critical
This is the highest-severity issue in the repository. Every other bug here costs a user
correctness or convenience. This one can cost them their balance, silently, without any bug in
SPulse's own source, and there is currently no control anywhere in the stack that would stop it or
even detect it.
Problem
Every line of code that builds, signs, and submits a transaction is fetched from a third-party CDN
at page load, over a URL with no integrity check:
frontend/soroban.js:3--const SDK_URL = "https://esm.sh/@stellar/stellar-sdk@14.5.0?bundle",loaded via
import(SDK_URL)on line 9frontend/wallet.js:14--import("https://esm.sh/@stellar/freighter-api@5.0.0?bundle")Neither is pinned by hash. A dynamic
import()of a remote URL cannot carry a SubresourceIntegrity attribute, so there is no mechanism verifying that the bytes esm.sh returns are the bytes
the package published.
And nothing constrains where scripts may be loaded from.
netlify.tomlsetsX-Content-Type-Options,Referrer-Policy, andPermissions-Policy, but noContent-Security-Policy-- confirmed against the live response headers fromhttps://stellar-pulse.netlify.app/. There is noscript-src, noconnect-src, nodefault-src.Impact
stellar-sdkis the module that constructs the transaction, andfreighter-apiis the module thathands it to the wallet for signature. If esm.sh is compromised, or its DNS or BGP is hijacked, or
its edge cache is poisoned for this origin, an attacker controls both. They can:
address instead of the market contract
place_betinvocation for atransferof the wallet's entire XLM balanceThe user's protection at that point is reading raw XDR in the Freighter confirmation dialog, which
in practice nobody does. This drains any wallet that connects to the site, and it requires no
vulnerability in this repo's own code -- only in a dependency delivery path this repo has chosen not
to control.
The exposure is not theoretical for a CDN of this kind: unpinned, unverified runtime module loading
from a public transpiling CDN is the exact shape of the polyfill.io class of incident.
What to do
@stellar/stellar-sdkand the wallet library into the repo, or self-host them from thesame origin, so the signing path ships with the site and is reviewable in git
<script>tag carrying aSubresource Integrity hash and
crossorigin, never a bare dynamicimport()of a URLContent-Security-Policyheader innetlify.tomlwith an explicitscript-src,connect-srclimited to the Soroban RPC and Horizon endpoints infrontend/contracts.json,object-src 'none', andbase-uri 'self'rather than a silent CDN change
frame-ancestors 'none'to stop the app being framed by a look-alike siteWhy this is critical
This is the highest-severity issue in the repository. Every other bug here costs a user
correctness or convenience. This one can cost them their balance, silently, without any bug in
SPulse's own source, and there is currently no control anywhere in the stack that would stop it or
even detect it.