Expose User-Agent allowlists and an explicit STB reset for managed customer devices. Enforce restriction permissions and ownership, serialize resets with binding checks, invalidate portal caches, and preserve User-Agent policy on reset.
Fix the CI style violation and keep new translations in en.ini only. Record pre-push validation and translation workflow guidance.
Add per-reseller bouquet limits and per-customer selection and ordering,
while keeping package defaults for new lines and trials.
Integrate the separately installed User Proxy module through core routing
and customer form hooks, with native playback fallback.
Fix connection-counter deep links across admin and reseller pages. Add
proxy availability to dashboard status and a setting to hide Active Codes.
Include migrations 094/095, setup guides and regression coverage. Keep
local skills, installed modules and development dependencies out of Git.
- get_user answered a sub-reseller's password hash and API key when
asked by name (show_columns is taken before the forced hide), and
failed on in_array(.., null) without hide_columns. getUser() now never
carries either field, for get_user and for the row create_user and
edit_user answer with.
- A reseller without allow_restrictions still set allowed addresses and
user agents, the blocked agents' bypass and the ISP lock on a new line
or device: the save skipped its restrictions block, but the new row
had been filled from what was posted. processData() drops the fields.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AneTzAHPPkYWPQjwyZ2td9
ResellerAPI::processData() is the whitelist every reseller line, MAG and
Enigma2 save goes through. Two fields in it could be turned on other
people's subscriptions:
- pair_id: a new line stored whatever id was sent. Pairing copies the paired
line's expiry and bouquets onto this one on every renewal
(MagService::syncLineDevices), so a reseller could buy the cheapest line
paired to another reseller's paying customer and ride that subscription
for free. MAG and Enigma2 saves already required the paired line to be
the reseller's own; now every type does.
- trial: accepted on an edit, where it reset the expiry for trial_credits
(usually 0). The trial quota counts lines by created_at, which an edit
does not change, so any existing line could be extended forever. The
forms only send it when creating; the API now does the same.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V2uBUbGApb4A7Rbcoi7dxA