Skip to content

Commit 35f32a0

Browse files
committed
docs: add SECURITY.md and CHANGELOG.md for v2.5.0
- SECURITY.md: reporter channel (GitHub PVR + email), supported versions, scope. Standard SECURITY.md so future reporters have a documented path and Dependabot / scanners surface the policy. - CHANGELOG.md: Keep-a-Changelog format opening with v2.5.0. The Security section leads with the WebRTC ingest issue (CVE pending). Fixed and Added sections summarise the audit work and the new transcoder visibility / pprof features that ship in this release. The GHSA advisory text was drafted alongside this commit but is intentionally kept out of the tracked tree (gitignored) — it telegraphs the WebRTC issue before the patched release is available, and there's no need for it to live on main once the advisory is published.
1 parent 13bb9fe commit 35f32a0

3 files changed

Lines changed: 234 additions & 0 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ bin/
1818
.gemini/
1919
.antigravity/
2020
.worktrees/
21+
.github/ADVISORY-DRAFT-*.md

CHANGELOG.md

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
# Changelog
2+
3+
All notable changes to this project are documented here.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [2.5.0] - 2026-05-09
9+
10+
### Security
11+
12+
- **CVE-PENDING (GHSA-xxxx-xxxx-xxxx)** — Missing authentication on
13+
the WebRTC source-ingest endpoint. `POST /webrtc/source-offer`
14+
accepted any inbound SDP offer with no source-password check;
15+
any internet user able to reach the server could hijack any
16+
mount's broadcast and replace the legitimate publisher's audio.
17+
The icecast SOURCE / RTMP / SRT ingest paths already required
18+
the per-mount source password — this one didn't. Affected
19+
versions: **>= 0.8.95, <= 2.4.1** (introduced 2026-02-21 in
20+
`e2b60d6`). Fixed in this release. CWE-306. CVSS 3.1: **7.4
21+
High** (`AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:L`). The same fix
22+
also adds source-password rate-limit hookup so wrong-password
23+
attempts contribute to the IP-level brute-force lockout.
24+
- Hardened additional auth gaps surfaced during the audit that
25+
shipped this release:
26+
- `POST /admin/golive/chunk` now requires CSRF + per-mount
27+
access. Previously, any authenticated user could broadcast
28+
raw audio bytes to any mount.
29+
- AutoDJ create / update / delete (both `/api/v2/autodjs` and
30+
the legacy `/admin/autodj/*` form handlers) now require
31+
`superadmin`. Previously a `dj`-role user could register an
32+
AutoDJ with arbitrary `song_command` / `on_play_command`
33+
shell strings that the server executes via `sh -c`
34+
privilege escalation to the tinyice service user.
35+
- `POST /api/pending-users/approve` and `/deny` now check CSRF.
36+
Without it, an attacker page that a logged-in admin visited
37+
could submit a form-encoded POST with a JSON body that the
38+
handler would still decode and use to promote an attacker-
39+
controlled pending user to `superadmin`.
40+
- `/admin/clear-auth-lockout` and `/admin/clear-scan-lockout`
41+
now require `admin` or `superadmin`. A `dj` could
42+
previously clear an attacker's brute-force lockout, undoing
43+
the rate-limiter.
44+
- `handleLogin` now runs bcrypt against a dummy hash for
45+
unknown users to close the account-enumeration timing
46+
oracle (~250 ms vs <1 ms).
47+
- `checkAuthLimit` now bypasses the lockout when the IP is
48+
whitelisted, so an operator's own IP can recover after a
49+
misconfiguration without restarting the service.
50+
- MPD `password` comparison now uses
51+
`crypto/subtle.ConstantTimeCompare`. The MPD
52+
`command_list_*` accumulator is now bounded at 10 000 lines
53+
so an authenticated client can't OOM the process by opening
54+
a list and never closing it.
55+
56+
### Fixed
57+
58+
- **Production hang root cause:** `FindNextPageBoundary` infinite
59+
loop at the circular-buffer wrap. When the search window was
60+
clamped to `≤ 3` bytes, the iterator advanced by `n - 3 ≤ 0`
61+
and never moved forward, holding `cb.mu.RLock` until the
62+
process restarted. Every concurrent `Buffer.Write` then queued
63+
on the buffer's write lock, which queued every `Stream.Snapshot`,
64+
which queued every `GetStream`, freezing the relay. r4dio's
65+
pprof showed one stuck listener and 100+ goroutines blocked
66+
behind it. Fix: never advance by less than 1 byte; skip the
67+
search when `n < magicLen`.
68+
- `Stream.Broadcast` could panic with `send on closed channel`
69+
when a listener's `Unsubscribe` raced with the listener-signal
70+
fan-out (production journal showed 4 occurrences in 7 days,
71+
all from the icecast SOURCE goroutine). Two-phase fix:
72+
recover() in the signal loop as a defense, and remove the
73+
`close(ch)` from `Unsubscribe` — every caller is a self-exit
74+
defer that doesn't need the wake-up signal. Eliminates the
75+
race entirely.
76+
- `Stream.SetCurrentSong` was holding `s.mu.Lock` across five
77+
GORM/SQLite queries inside `History.Add`. Every ICY
78+
metadata update froze every broadcast / subscribe / snapshot
79+
/ listener handler on the stream for the duration. Capture
80+
the diff under the lock, release, then call `History.Add`
81+
outside.
82+
- Icecast SOURCE hijacked TCP connections now have a 60s idle
83+
read deadline; a silent encoder (NAT idle drop, frozen
84+
process) used to pin a goroutine + FD + mounted Stream
85+
forever, eventually exhausting FDs.
86+
- Same idle-read deadline now also applies to the icecast
87+
pull-relay body, RTMP per-conn reads, and SRT publish reads.
88+
- WebRTC `OnConnectionStateChange` is now registered ONCE per
89+
PeerConnection (in HandleWHEPOffer / HandleOffer) rather than
90+
by both `streamToTrack` and `streamVideoToTrack` — pion's
91+
API replaces the prior handler, so the loser was leaking a
92+
goroutine + Stream subscription per WHEP listener disconnect.
93+
- WebRTC source-ingest now drains the previous publisher's pump
94+
goroutine (up to 3s) before letting the successor start
95+
writing — without this, two pumps briefly ran in parallel and
96+
produced torn Ogg pages on listener tabs.
97+
- `pc.Close()` is now called explicitly in the WebRTC terminal
98+
state-change handler to release pion's UDP sockets / DTLS
99+
state.
100+
- `Track.ResolveCodec` no longer panics on a nil receiver. The
101+
function's nil-check branch dereferenced `t.Codec` after
102+
asserting `t == nil`.
103+
- `SavePlaylist` no longer takes `s.mu.RLock` recursively via
104+
`GetSongTitle`. Recursive RLock is undefined behaviour in
105+
Go's writer-preferring `RWMutex` and deadlocks if a writer
106+
queues between the outer and inner acquisitions.
107+
- `Pipeline.Stats` now uses `atomic.LoadInt64` for `BytesIn` /
108+
`BytesOut` (matching the atomic writes from `Broadcast`) and
109+
`Stream.GetLastDataReceived` / `GetOggHead` synchronise
110+
reads of those fields against their locked writes (avoids
111+
torn `time.Time` and torn slice headers).
112+
- TS demuxer now resyncs byte-by-byte when the sync byte is
113+
missing instead of jumping by 188 — silent data loss on
114+
misaligned SRT inputs is gone.
115+
- HLS `RegisterHLS` is now race-safe; two concurrent first
116+
listeners no longer each spawn their own `segmentLoop`.
117+
- `decoder_hub.contextDeadline` no longer leaks a 30 s goroutine
118+
per pump cycle (replaced with `time.After`).
119+
- `OnTrackStart` callback runs in a goroutine outside any HTTP
120+
handler — recover() now contains panics in user-supplied
121+
webhook subscribers so they can't crash the process.
122+
- `Streamer.Stop` now cancels the streamer-lifetime context, so
123+
`on_play_command` child shells get SIGKILL via
124+
`exec.CommandContext` instead of lingering up to their
125+
per-command timeout (default 10s) past the operator's Stop.
126+
- YP directory `POST` now has a 15 s context timeout. Previously
127+
used `http.PostForm` against the default client with no
128+
timeout, so a hung directory server pinned the
129+
`directoryReportingTask` goroutine forever.
130+
131+
### Added
132+
133+
- `feat(transcoder): per-output visibility` — TranscoderConfig has
134+
a new `visibility` field with `""` (follow input, default),
135+
`"public"` (listed), or `"unlisted"` (hidden, still
136+
streamable). Surfaces in the admin add-transcoder form and
137+
the v2 API.
138+
- `feat(metrics): /debug/pprof on the metrics server` — the
139+
metrics-server bootstrap function existed but was never
140+
called from `Server.Start()`. Now it is, and it also
141+
registers `net/http/pprof` so a stuck production instance
142+
can be triaged with `curl
143+
http://HOST:8081/debug/pprof/goroutine?debug=2` etc.
144+
Mutex- and block-profile sampling is enabled at rate 1.
145+
146+
### Changed
147+
148+
- `relay.Broadcast` now releases the stream's write lock before
149+
fanning out listener signal channels (committed earlier in
150+
this release line as `daf5368`). The previous full-lock fan-
151+
out was the dominant lock-contention vector under load.
152+
153+
[2.5.0]: https://github.com/DatanoiseTV/tinyice/releases/tag/v2.5.0

SECURITY.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Security Policy
2+
3+
## Reporting a vulnerability
4+
5+
If you've found a vulnerability in TinyIce — particularly anything that
6+
lets an unauthenticated user inject content into a mount, exfiltrate
7+
listener data, escalate privileges, or crash the server remotely —
8+
please **do not** open a public issue.
9+
10+
Instead:
11+
12+
1. Use GitHub's [private vulnerability reporting][gh-pvr] on this
13+
repository (Security tab → "Report a vulnerability"). This
14+
encrypts the report and routes it directly to maintainers.
15+
2. Or email **6614616+DatanoiseTV@users.noreply.github.com** with the
16+
word `SECURITY` in the subject line.
17+
18+
Please include:
19+
20+
- A description of the issue and what an attacker can do with it.
21+
- Affected versions (and ideally the specific commit, if you've
22+
bisected it).
23+
- A proof-of-concept or reproduction steps if you have one.
24+
- Whether you'd like to be credited in the published advisory.
25+
26+
We'll acknowledge receipt within 72 hours and aim to ship a patched
27+
release within 7 days for High/Critical, longer for lower severities.
28+
We use GitHub's CVE Numbering Authority path, so once an advisory is
29+
ready it will get a CVE ID assigned and pushed to the National
30+
Vulnerability Database.
31+
32+
[gh-pvr]: https://github.com/DatanoiseTV/tinyice/security/advisories/new
33+
34+
## Supported versions
35+
36+
We patch security issues on the latest minor release line only.
37+
Earlier minor lines may be backported on a best-effort basis at the
38+
maintainers' discretion (high-severity issues, low backport cost).
39+
40+
| Version | Supported |
41+
|---------|--------------------|
42+
| 2.5.x | :white_check_mark: |
43+
| 2.4.x | :warning: critical fixes only |
44+
| < 2.4 | :x: |
45+
46+
If you are running a release earlier than 2.4, upgrade.
47+
48+
## Scope
49+
50+
In scope:
51+
52+
- Authentication and authorization bypasses on any endpoint.
53+
- Stream injection, listener data exfiltration, or content tampering
54+
affecting other users of the same TinyIce instance.
55+
- Remote code execution, command injection, path traversal.
56+
- Server-side request forgery (SSRF) reachable from a network attacker.
57+
- Denial of service that an unauthenticated attacker can trigger
58+
with a small request rate (a single packet that crashes the
59+
server, or a request that causes unbounded resource use).
60+
61+
Out of scope (please don't report):
62+
63+
- DoS by flooding the server with traffic. Tinyice is a streaming
64+
server; bandwidth is finite.
65+
- Vulnerabilities in third-party dependencies that don't affect any
66+
reachable code path in TinyIce.
67+
- Self-XSS or social-engineering scenarios that require an admin to
68+
paste hostile input into their own admin UI.
69+
- Findings that require a malicious operator to harm their own server.
70+
- Best-practice complaints (missing security headers, weak TLS
71+
ciphers when the operator can configure them) — open a regular
72+
issue or PR.
73+
74+
## Hall of Fame
75+
76+
Contributors who responsibly disclosed real issues will be credited
77+
here once their advisory is published, unless they ask to remain
78+
anonymous.
79+
80+
(no published advisories yet)

0 commit comments

Comments
 (0)