Skip to content

Fix Discogs internal ID tag leaking into "View on Discogs" links - #1204

Open
RiceTeaPrince wants to merge 1 commit into
Nezreka:devfrom
RiceTeaPrince:fix/discogs-view-link-tag-leak
Open

Fix Discogs internal ID tag leaking into "View on Discogs" links#1204
RiceTeaPrince wants to merge 1 commit into
Nezreka:devfrom
RiceTeaPrince:fix/discogs-view-link-tag-leak

Conversation

@RiceTeaPrince

Copy link
Copy Markdown

Fixes #1203

Problem

The m/r type-tag _tag_discogs_album_id() adds to a Discogs album ID
(introduced in #848 to fix the master/release collision in #847) is meant
to stay an internal routing detail for _discogs_album_endpoints(). It
never gets stripped back off before reaching a public-facing field, and
leaks out through two independent paths, both producing the same symptom —
clicking "View on Discogs" for an album opens a 404:

  • Broken: https://www.discogs.com/release/r5743831
  • Correct: https://www.discogs.com/release/5743831 (bare numeric ID
    redirects to Discogs' own canonical slugged URL — no need to construct
    the slug ourselves)

Leak site 1_build_artist_detail_release_card()
(core/metadata/discography.py), which builds cards for the artist-detail
discography grid, drops external_urls entirely from its output, even
though Album.from_discogs_release() already computes the correct slugged
URL at parse time (core/discogs_client.py:346-348). The card is left with
only the tagged id.

Leak site 2core/discogs_worker.py's _update_album(), the
background auto-match worker, persists the tagged ID straight into the
albums.discogs_id DB column. That column is read back verbatim (no
un-tagging) by get_artist_full_detail()
(database/music_database.py:15286, album_data = dict(album_row)) and
handed to the frontend, where getServiceUrl()
(webui/src/routes/artist-detail/-artist-detail.enhanced-album.ts:56-59)
does a plain string interpolation with no idea the tag exists. This is the
one that actually fires for an already-matched library album's "View on
Discogs" badge, not just an unowned discography-grid card.

Fix

Two independent, additive changes — neither touches the tagging scheme
itself or the DB's stored value, so _discogs_album_endpoints()'s routing
is unaffected:

  • core/metadata/discography.py_build_artist_detail_release_card()
    now passes through external_urls (both the typed-converter and legacy
    duck-typed branches) instead of dropping it.
  • core/discogs_client.py — new _untag_discogs_album_id() helper
    (inverse of the existing _tag_discogs_album_id()), display-only.
  • database/music_database.pyget_artist_full_detail() strips the tag
    off album_data['discogs_id'] via the new helper right before the album
    dict is returned, so the API response is always a clean numeric ID.

_discogs_album_endpoints() already has a documented "legacy untagged ID"
fallback (release-first, then master) built specifically to self-heal
pre-#848 bad matches, so an untagged ID reaching it by some other path
still resolves correctly rather than failing outright.

Tests

  • tests/test_discogs_id_typing.py (+6, alongside the existing coverage
    from Discogs: fix master/release ID collision fetching the wrong album #848) — _untag_discogs_album_id(): round-trips with the tagger,
    release/master stripping, empty/None, an already-untagged legacy ID left
    inert, a non-Discogs ID string left untouched if the helper is ever
    misapplied, and a check that an untagged ID still resolves correctly via
    _discogs_album_endpoints()'s existing legacy fallback if it's ever
    re-fed into it.
  • tests/metadata/test_discography_typed_path.py (+2, +1 extended
    assertion) — _build_artist_detail_release_card()'s external_urls
    passthrough for both the typed-converter and legacy duck-typed branches
    (the latter using this issue's real reported album), plus a
    no-external_urls case defaulting to {} rather than crashing.
  • tests/test_artist_full_detail_discogs_id_untag.py (new file, +4) — an
    isolated in-memory-sqlite regression test, same pattern as
    tests/test_artist_full_detail_source_id.py: release-tagged,
    master-tagged, missing, and legacy-untagged discogs_id values all come
    back correctly from get_artist_full_detail().

Verification

Both fixes' logic (not just the diagnosis) checked against a real
Discogs API response for the originally-reported release
(GET /releases/5743831, Naoya Matsuoka — Watermelon Dandies):

  • The response's real uri field, run through the external_urls fix,
    produces exactly
    https://www.discogs.com/release/5743831-Naoya-Matsuoka-Watermelon-Dandies.
  • The same response, run through the existing tagging logic, reproduces the
    exact reported broken ID (r5743831); run through
    _untag_discogs_album_id(), correctly strips to 5743831.
  • _discogs_album_endpoints('5743831') (untagged) still resolves to
    ['/releases/5743831', '/masters/5743831'] — release-first, matching the
    existing self-heal fallback — confirming the untag can't break routing
    even if re-fed into it.

Ran this repo's actual CI gates locally (Python 3.11.15 via uv,
requirements-dev.txt pinned versions, webui/ deps via npm ci since
tests/test_vanilla_globals_resolve.py needs a real pinned oxlint):

  • ruff check . — clean
  • compileall api core database services scripts web_server.py wsgi.py beatport_unified_scraper.py — clean
  • pytest — 15159 passed, 2 skipped, 7 deselected, 0 failed (15147 baseline + this PR's 12 new tests)

Not independently confirmed in a real browser from this environment —
curl against all three URL variants (tagged, bare numeric, full slug) hit
Discogs' Cloudflare managed-challenge page identically regardless of which
one is correct, so it's not a usable signal. The one real-browser
confirmation is the reporter's own manual test (removing the leading r
was sufficient to make the link resolve), consistent with the API/code-level
checks above.

… on Discogs" links

Two independent leaks, both from the m/r type-tag _tag_discogs_album_id()
adds (introduced in Nezreka#848): the artist-detail discography card builder never
passed through the already-computed external_urls, and get_artist_full_detail
read the tagged discogs_id DB column back verbatim for the frontend badge.
Both fixed additively; the tag itself and _discogs_album_endpoints() routing
are untouched.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant