Skip to content

The transaction signing path loads its code from a third-party CDN at runtime, with no SRI and no CSP #211

Description

@Muyideen-js

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

  • Vendor @stellar/stellar-sdk and the wallet library into the repo, or self-host them from the
    same origin, so the signing path ships with the site and is reviewable in git
  • If a CDN is kept for any asset, load it with a static <script> tag carrying a
    Subresource Integrity hash and crossorigin, never a bare dynamic import() of a URL
  • Add a Content-Security-Policy header in netlify.toml with an explicit script-src,
    connect-src limited to the Soroban RPC and Horizon endpoints in frontend/contracts.json,
    object-src 'none', and base-uri 'self'
  • Pin exact dependency versions and record their hashes, so an upgrade is a reviewed commit
    rather than a silent CDN change
  • Add frame-ancestors 'none' to stop the app being framed by a look-alike site

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.

Activity

  1. added
    bugSomething isn't working
    priority: criticalCritical to protocol safety or mainnet readiness
    area: securitySecurity engineering and adversarial analysis
    area: infrastructureRPC, indexing, operations, and reliability
    on Sep 8, 2026
  2. guptakumarranjeet150 commented on Sep 8, 2026

    @guptakumarranjeet150
    Contributor

    Hello @SPulse-Org team,

    I would love to take on this security issue regarding third-party CDN script loading without SRI and CSP pinning.

    Proposed Solution:

    1. Subresource Integrity (SRI): Compute and pin cryptographic hashes (sha384/sha512) for external signing scripts.
    2. Strict Content-Security-Policy (CSP): Restrict script-src directives to explicit origin domains with fallback error handlers.
    3. Automated Security Tests: Add unit tests verifying SRI hash validation failure triggers and safe CSP policy headers.

    Ready to submit a clean PR with passing tests within 12–24 hours. Please assign to me!

  3. guptakumarranjeet150 commented on Sep 8, 2026

    @guptakumarranjeet150
    Contributor

    Hi everyone,

    I can tackle The transaction signing path loads its code from a third-party CDN at runtime, with no SRI and no CSP. Proposed implementation roadmap:

    • Analysis: Inspect current behavior and edge cases in SPulse-Org/SPulse
    • Implementation: Deliver a clean, modular solution for The transaction signing path loads its code from a third-party CDN at runtime, with no SRI and no CSP strictly following SPulse-Org/SPulse conventions.
    • Testing: Cover changes with automated tests ensuring CI passes smoothly.
    • PR: Atomic commit with clean git hygiene and passing CI

    Looking forward to contributing—please feel free to assign!

  4. Iker2522 commented on Sep 8, 2026

    @Iker2522

    I'd like to take this issue on!

    I can fix this critical security vulnerability by vendoring the transaction signing dependencies and configuring robust Security Headers for SPulse-Org / SPulse.

    I will eliminate remote dynamic imports from esm.sh in frontend/soroban.js and frontend/wallet.js, replacing them with locally vendored, version-pinned modules for @stellar/stellar-sdk and @stellar/freighter-api. I will also update netlify.toml to enforce a strict Content-Security-Policy with explicit script-src, connect-src locked to approved RPC/Horizon endpoints from frontend/contracts.json, object-src 'none', base-uri 'self', and frame-ancestors 'none' to block framing attacks.

    I will verify the entire transaction lifecycle locally and in preview deployments to ensure wallet connections, XDR building, and contract submissions function flawlessly without pulling any unverified remote scripts. PR ready in 1 day. Closes #211.

  5. grantfox-oss commented on Sep 12, 2026

    @grantfox-oss

    🎉 This issue has been marked as completed on GrantFox!

    @guptakumarranjeet150's PR #213 was approved and merged by @Muyideen-js.

    🏆 @guptakumarranjeet150: You earned 35 FoxPoints for this contribution! Your current tier: Explorer (35 total points). Track your full progress on GrantFox.

    👏 Great work, @guptakumarranjeet150! Keep contributing to SPulse-Org.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: infrastructureRPC, indexing, operations, and reliabilityarea: securitySecurity engineering and adversarial analysisbugSomething isn't workingpriority: criticalCritical to protocol safety or mainnet readiness

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions