v1.9.7
v1.9.7
Security-only release. No new features, no UI changes, no data migration. Built on the v1.9.6 plugin behavior; install in place.
Security fixes
This release closes findings from a code-level security review (see SECURITY_REVIEW.md) plus the issues CodeQL's first run on the repo flagged.
Higher-severity fixes
- Per-user attachment storage quota (was unbounded). An authenticated user could spam chat attachment uploads — each within the existing 8 MB per-file cap — until your disk filled. Now bounded at 200 files / 200 MB total per user. Existing attachments are unaffected; the next upload that would cross the cap returns a clean error message asking the user to delete old files first. (Services/MessagingService.cs)
- Anonymous profile-card CSP tightened.
script-src 'unsafe-inline'removed from the public profile-card endpoint's Content-Security-Policy. The template never used inline scripts, so the allowance was pure defense-in-depth weakening.style-src 'unsafe-inline'is still present (the embedded<style>block uses per-render template variables and migrating to a nonce-based allowlist is a follow-up). (Api/AchievementBadgesController.cs)
Defense-in-depth + correctness
- SVG sanitizer extended:
<animate>and<set>SMIL animation elements now blocked alongside<script>/<foreignObject>/<iframe>/<embed>/<object>. SMIL animation can mutate an attribute at render time (e.g., animatehrefto ajavascript:URI on SVG-capable clients) past the static post-parse scan. - Webhook URL DNS resolution bounded at 3s. Previously
Dns.GetHostAddresseswas synchronous with no caller-side timeout — a slow resolver could stall an admin request for the OS DNS-client default (5-30 s). Times out cleanly now and fails-closed. - Audit-log endpoint
?limit=clamped at the controller layer to[1, 1000]. Service-level cap (5000 max entries) is unchanged; this just stopsint.MaxValuefrom forcing the larger O(N) response.
Resource cleanup
MessagingServiceandAchievementBadgeServicenow implementIDisposable. Both classes own aTimer(the debounced-save throttle). WithoutIDisposable, the Timer's delegate kept the singleton rooted across plugin reloads — a slow leak. Now released cleanly on plugin unload.
CI / CodeQL / Scorecard
- CodeQL first scan baselined. 100+ alerts triaged: real findings fixed above; the rest are ASP.NET structured-logging false positives (
cs/log-forging), graceful-degradation patterns (cs/empty-catch-block,cs/catch-of-all-exceptions), and style-only hints already covered by NetAnalyzers. Suppression list with per-rule justifications in .github/codeql/codeql-config.yml. - Scorecard Pinned-Dependencies fix: the existing
security.ymlworkflow now SHA-pinsactions/checkout,actions/setup-dotnet, andgitleaks/gitleaks-actioninstead of using floating tags. Brings the workflow in line with the rest of the CI fleet (ci.yml / codeql.yml / scorecard.yml / release.yml were already SHA-pinned).
Deferred (acknowledged, not in this release)
style-src 'unsafe-inline'on the anonymous profile-card endpoint (requires nonce migration of the templated<style>block).- Audit-log
detailsfield still uses string concatenation with raw usernames. JSON serialization at file-level escapes correctly, so the risk is parsing ambiguity not log injection. Will swap to a structuredDetailsobject in a follow-up. FriendsSimpleModeadmin toggle enumerates all server users — documented behavior, no code change planned.
Upgrade
Drop-in replacement. No config changes, no data migration. Existing chat attachments are unaffected by the new quota (only new uploads are checked).
Package checksums
- MD5: see
Jellyfin.Plugin.AchievementBadges_1.9.7.0.md5 - SHA256: see
Jellyfin.Plugin.AchievementBadges_1.9.7.0.sha256
Sigstore-signed + SLSA build-provenance attested. Verify with cosign verify-blob or gh attestation verify.