mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-12 12:06:16 -08:00
fix(stalker): send cmd in the reference MAG wire format
A real MAG sends cmd unencoded and the portal decodes its query exactly
once, so a cmd that already contains percent sequences (%3A tokens,
pre-encoded path segments) must pass through untouched. The previous
encodeURIComponent transport (2c032cd3c, 0.22) double-encoded such cmds
(%3A -> %253A): strict portals and reseller panels that compare cmd
literally, and stock create_link handlers matching the decoded value,
saw a different string than a real STB sends.
The new shared encodeStalkerCmdValue() reproduces the reference wire
bytes: % passes through verbatim, characters the WHATWG URL serializer
keeps raw in a query stay raw (so the bytes survive the axios/new URL
transport unchanged), and everything else is percent-encoded. That
preserves the 0.22 injection protection - &, # (and ; for PHP setups
with a ; argument separator) inside cmd cannot append or truncate query
parameters; they decode back to the original byte server-side.
Both transports now share the format: the Electron query builder is
extracted to buildStalkerRequestUrl() and the web-backend /stalker
proxy appends cmd to the portal URL itself instead of letting axios
turn slashes into %2F (the opposite divergence).
Also unifies the two divergent response-side cmd normalizers: the
cross-portal collection resolver now uses the Stalker store's
normalizeStalkerPlaybackCommand/resolveStalkerPlaybackUrl, so playing
from Favorites/global collections resolves relative (/media/...) and
query-only (?token=...) create_link replies against the portal base
instead of handing the player a bare relative path.
The mock portal's create_link response gains mock-only cmd_received/
query_keys_received diagnostics; a new Electron e2e pins the contract
end-to-end (single decode, injection blocked). Unit corpus tests cover
the encoder, the Electron builder, the web-backend proxy, and the
resolver.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
04b2f9b82e
commit
3326f73388
15 files changed
+600
-59
No files matched your search
@@ -208,13 +208,21 @@ marker and an `ffrt4://radio/...` command.
|
||||
"cmd": "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8",
|
||||
"streamer_id": "1",
|
||||
"load": "",
|
||||
"error": ""
|
||||
"error": "",
|
||||
"cmd_received": "ffrt4://ch/live/1001/index.m3u8",
|
||||
"query_keys_received": ["JsHttpRequest", "action", "cmd", "type"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The stream URL is selected from a pool of 4 real public HLS test streams. The choice is deterministic based on the `cmd` field's character sum, so the same item always returns the same stream.
|
||||
|
||||
`cmd_received` and `query_keys_received` are mock-only diagnostics (a real
|
||||
portal does not send them): they echo the request's `cmd` after Express' single
|
||||
query decode — the same view a PHP portal gets from `$_GET` — plus the sorted
|
||||
set of query keys. E2E uses them to pin the client's `cmd` wire contract: no
|
||||
double-encoding, and no query-parameter injection through `cmd`.
|
||||
|
||||
### `get_short_epg`
|
||||
|
||||
```json
|
||||
|
||||
@@ -138,6 +138,51 @@ blank fields are not generated or forwarded to `get_profile`.
|
||||
metadata is independent from M3U playlist EPG metadata and must not depend on
|
||||
M3U-specific EPG fields.
|
||||
|
||||
## Request Transport and `cmd` Encoding
|
||||
|
||||
A real MAG/STB sends `cmd` unencoded: the portal's client JS concatenates raw
|
||||
`key=value` pairs, the browser URL layer escapes only what a URL cannot carry,
|
||||
and PHP's `$_GET` applies exactly one form-urldecode. The portal therefore sees
|
||||
the stored `cmd` decoded **once** — a pre-encoded `%3A` arrives as `:` and a
|
||||
literal `+` arrives as a space. IPTVnator reproduces that reference wire format
|
||||
on both transports with the shared `encodeStalkerCmdValue()`
|
||||
(`libs/shared/interfaces/src/lib/stalker-cmd-encoding.util.ts`):
|
||||
|
||||
- `%` passes through untouched, so a `cmd` that already contains percent
|
||||
sequences is never double-encoded (the pre-0.23 `encodeURIComponent`
|
||||
transport delivered `%253A` and strict panels no longer matched the string).
|
||||
- Characters the WHATWG URL serializer keeps raw in a query stay raw
|
||||
(`/ : ? = + , @ $ [ ]` …), so the emitted bytes survive the axios/`new URL`
|
||||
transport unchanged.
|
||||
- Everything else is percent-encoded. This keeps the injection protection from
|
||||
the 0.22 hardening: `&`, `#` (and `;` for PHP setups with a `;` argument
|
||||
separator) inside `cmd` cannot append or truncate query parameters — they
|
||||
decode back to the original byte server-side, so the portal-visible value is
|
||||
unaffected.
|
||||
|
||||
Consumers of the encoder:
|
||||
|
||||
- Electron: `buildStalkerRequestUrl()`
|
||||
(`apps/electron-backend/src/app/events/stalker-request-url.ts`) assembles the
|
||||
portal query for `STALKER_REQUEST`; `cmd` uses the reference encoding, every
|
||||
other param stays fully `encodeURIComponent`-encoded, and `JsHttpRequest=1-xml`
|
||||
is appended when missing.
|
||||
- PWA: the web-backend `/stalker` proxy appends `cmd` to the portal URL with
|
||||
the same encoder instead of letting axios' serializer turn slashes into
|
||||
`%2F`. The renderer→proxy leg uses `URLSearchParams`, which Express decodes
|
||||
losslessly, so the stored `cmd` string reaches the proxy intact.
|
||||
|
||||
The mock portal's `create_link` response carries mock-only `cmd_received` and
|
||||
`query_keys_received` diagnostics so E2E can pin this contract
|
||||
(`apps/electron-backend-e2e/src/providers.e2e.ts`).
|
||||
|
||||
Response-side `cmd` normalization is also shared: both the Stalker store and
|
||||
the cross-portal collection resolver (`StreamResolverService`) use
|
||||
`normalizeStalkerPlaybackCommand()` / `resolveStalkerPlaybackUrl()` from
|
||||
`libs/portal/stalker/data-access`, which strip the `<solution> ` prefix and
|
||||
resolve relative (`/media/...`) or query-only (`?token=...`) `create_link`
|
||||
replies against the portal base URL.
|
||||
|
||||
## Live TV and Radio
|
||||
|
||||
The Stalker live route and radio route intentionally share
|
||||
|
||||
Reference in new issue
Block a user