Is PocketTube: Youtube Subscription Manager safe?

Medium risk

PocketTube is medium risk. PocketTube sends the billing email and Paddle license tokens to a developer server, p.yousub.info, when a companion extension asks for paid status and the cached record isn't active, as a plain unencrypted form post.

Dmytro Nabokv18.9.0Chrome Web Store
45Risk
Who publishes it

Dmytro Nabok - 1 other listing from the same operator, none carrying a finding

What this publisher told the store about itself, and the other listings that told it the same thing.

Publisher
Dmytro Nabok

Same store account

1 other listing published from this account, 60k+ users between them, none of them carrying a finding.

Shared hosts - 5 hostnames

Hostnames hardcoded in this extension that few other listings call. That can mean one operator behind both, and it can equally mean a small shared vendor, so it is context rather than a conclusion. Hosts that many listings call are left out: they are services, not connections.

he-tree-vue.phphe.com
Also called by 4 other listings, including PocketTube: Youtube Subscription Manager
i.yousub.info
Also called by 4 other listings, including PocketTube: Youtube Subscription Manager
p.yousub.info
Also called by 4 other listings
pockettube.io
Also called by 4 other listings, including PocketTube: Youtube Subscription Manager
yousub.info
Also called by 4 other listings, including PocketTube: Youtube Subscription Manager

AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.

Publishers can request a review.

Findings

PocketTube Sends Your Paddle Billing Email and License Tokens to Its Own Server

PocketTube sends the billing email and Paddle license tokens to a developer server, p.yousub.info, when a companion extension asks for paid status and the cached record isn't active, as a plain unencrypted form post.

Severity
Medium unwanted
Type
Unexpected
CWE
CWE-522
Source
Dynamic sandbox
What actually happens
You did this

One of the developer's own companion PocketTube extensions, already installed in the browser, sends this extension a message asking for the user's paid subscription status.

The message is only accepted from four specific extension IDs belonging to the same publisher, not from arbitrary websites or extensions.

The extension did this

PocketTube reads its locally cached Paddle subscription record, and if it doesn't already show active status, posts the billing email and license tokens to its own server.

The request carries the email address, every stored license token, a browser-type flag, and a partial-data flag.

Fields sent to p.yousub.info/paddle/email
  • Your billing email
    jordan.lee@gmail.com (illustrative)

    The email address tied to the Paddle purchase, sent in the clear as a form field.

  • Your license tokens
    PDL-8f3a1c-9e21b4 (illustrative)

    Every subscription token stored for the account, sent as repeated t[] form fields; the server uses these to recognize the account.

  • Browser type
    f

    A short code identifying which browser is making the request.

  • Partial-data flag
    y

    A fixed flag telling the server this is a partial re-check rather than a first registration.

Captured request
POSThttps://p.yousub.info/paddle/email

200 OK. The JSON response is merged back into the extension's locally cached subscription record.

Body
email=<planted marker email>&t[]=<planted marker token>&type=f&partial=y
The code that does this

The sender check and the paddle/email POST, as shipped vs. formatted for readability

Readable version

Sender-identity gate on the external message listener

build/background.js
case 114:  if ("bplnofkhjdphoihfkfcddikgmecfehdd" === a.id || "playlist@yousub.info" === a.id || "ehbcnddflnbfekidfnieclbpknhkelkj" === a.id || "noeichdeafhgfdbnhbldnheclppjknhm" === a.id) {    e.next = 118;    break  }  if (!ku()) {    e.next = 117;    break  }  return e.abrupt("return");case 117:  return e.abrupt("return", !0);case 118:

The POST to p.yousub.info/paddle/email

build/background.js
function wl() {  return wl = o(s().mark((function e(t) {    return s().wrap((function(e) {      for (;;) switch (e.prev = e.next) {        case 0:          return e.abrupt("return", new Promise(function() {            var e = o(s().mark((function e(a) {              var n, i, o, r, u, l, d, c, m;              return s().wrap((function(e) {                for (;;) switch (e.prev = e.next) {                  case 0:                    n = new AbortController, i = setTimeout((function() {                      return n.abort()                    }), 15e3), (o = new FormData).append("email", t.email), r = hl(t.tokens);                    try {                      for (r.s(); !(u = r.n()).done;) l = u.value, o.append("t[]", l)                    } catch (e) {                      r.e(e)                    } finally {                      r.f()                    }                    return o.append("type", Ml()), o.append("partial", "y"), e.next = 10, fetch("https://p.yousub.info/paddle/email", {                      body: o,                      method: "POST",                      mode: "cors",                      signal: n.signal                    });                  case 10:                    if (d = e.sent, clearTimeout(i), 200 === d.status) {                      e.next = 15;                      break                    }                    return a({                      data: Object.assign({}, t)                    }), e.abrupt("return");                  case 15:                    return e.next = 17, d.json();                  case 17:                    return c = e.sent, e.next = 20, Tl();                  case 20:                    if (m = e.sent, 0 !== Object.keys(c || {}).length) {                      e.next = 27;                      break                    }                    if (chrome.storage.local.set({                        api_counter: ++m                      }), !(m > 30)) {                      e.next = 27;                      break                    }                    return chrome.storage.local.set({                      api_counter: 0                    }), a({                      data: {                        error: "race-limit"                      }                    }), e.abrupt("return");                  case 27:                    a({                      data: Object.assign({}, t, c)                    });                  case 28:                  case "end":                    return e.stop()                }              }), e)            })));            return function(t) {              return e.apply(this, arguments)            }          }()));        case 1:        case "end":          return e.stop()      }    }), e)  }))), wl.apply(this, arguments)}
Where the data ends up
    • p.yousub.info

    Subscription-verification backend run by PocketTube's own developer (yousub.info), not an independent third party, but the email and license tokens still leave the device.

Updated 30 September 2026kdmnjgijlmjgmimahnillepgcgeemffb