Skip to content

Pro: CDN payload is installed without any integrity verification — please publish checksums or signatures #579

@Elehiggle

Description

@Elehiggle

What happened?

First off: not a functional bug - Uniwind Pro works great for us. This is a supply-chain hardening report for the Pro distribution channel.

The uniwind-pro npm package only contains the CLI (12 files, ~29 KB), so npm/pnpm lockfile integrity covers just that. The actual Pro payload - dist/ runtime, cxx/C++, android/ Kotlin, ios/ Swift, nitrogen/ (~888 files) - is downloaded at install time from cdn.uniwind.dev and extracted over node_modules/uniwind with nointegrity verification of any kind:

  • downloadFromCI in the published CLI does a plain fetch(cdnUrl) → tar extract → copy. No hash check, no signature check, no manifest.
  • The payload is also cached unverified in ~/.uniwind/cache/pro/<version> and blindly restored from there on later installs.
  • There is nothing to verify against even if we wanted to: no published checksums in the docs or dashboard, no .sha256/.sig sibling endpoints on the CDN (we probed - 400/404), no Content-Digest/Repr-Digest headers (RFC 9530), no Sigstore/PGP, and If-None-Match is not honored.

Consequence: a compromise of the CDN or your build pipeline would deliver arbitrary native code that gets compiled into our shipped iOS/Android binaries and executed on every dev machine and CI runner. We currently work around this with trust-on-first-use SHA-256 pinning (for reference, the 1.3.0 CI tarball hashes to 433778bcb26b9e7e404a8d1d57f586a4ba1808eb4770e129f7c4e2a7a8ced3b3, ETag/MD5 8099197de53e9beb749433d2218703b8), but that means a manual pin update on every version bump.

You already publish SLSA provenance attestations for the free uniwind package on npm, so the tooling is clearly within reach. Concrete asks, roughly in order of effort:

  1. A publicly reachable per-version checksum endpoint (e.g. cdn.uniwind.dev/uniwind-pro/checksums/<version>.sha256). Ideally the CLI then verifies before extracting and before restoring from cache.
  2. Content-Digest/Repr-Digest response headers on the tarball + honoring If-None-Match.
  3. Longer term: a Sigstore/cosign signature for the payload, or distributing Pro as a private npm package (lockfile integrity + provenance for free).
  4. Accepting the license token via an Authorization header - today the token in the URL path is the only auth mechanism (an Authorization: Bearer request 404s), so it ends up in CDN/proxy access logs.
  5. A security.txt/documented security contact for reports.

Steps to Reproduce

  1. Install uniwind-pro per the docs with a valid CI/CD license token (postinstall downloads https://cdn.uniwind.dev/uniwind-pro/ci/<version>/<token>).
  2. Inspect pre/postinstallinstall.jsdownloadFromCI in the published npm package: plain fetch → tar.x({ strip: 1 }) → copy over node_modules/uniwind; no hash/signature verification; unverified cache restore from ~/.uniwind/cache/pro/<version>.
  3. Probe for integrity sources next to the tarball URL (.sha256, .sig, .asc, /checksums/<version>, /manifest.json) → 400/404. Check response headers → no Content-Digest/Repr-Digest; If-None-Match returns 200 instead of 304.
  4. Check the npm package uniwind-pro@1.3.0: CLI only (~29 KB) - lockfile integrity never covers the native payload.

Snack or Repository Link (Optional)

No response

Uniwind version

uniwind-pro 1.3.0 (installed via npm alias "uniwind": "npm:uniwind-pro@^1.3.0")

React Native Version

0.85.3

Platforms

Android, iOS, Web

Expo

Yes

Additional information

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions