Skip to content

fix(marketplace): browser plugin installs were refused — accept the repository id the UI sends - #48

Merged
tajanthind merged 1 commit into
mainfrom
fix/install-source-repo-id-coercion
Sep 10, 2026
Merged

tajanthind merged 1 commit into
mainfrom
fix/install-source-repo-id-coercion

Conversation

@tajanthind

Copy link
Copy Markdown
Owner

The failure (explained from the live container logs + reproduction)

The access log showed:

2026-09-10 02:19:33 "POST /api/plugins/install HTTP/1.1" 400 406 0.005043

A 400, 5 ms, no download attempt. Reproducing it identified a real regression introduced when repo-scoped provenance shipped:

POST /api/plugins/install {"source_repo_id": "1"}   -> 400 in 0.0039s
  {"error":"fnack.discord-webhook is not published by repository 1 — it is published by: Official fnack Plugins (id 1)"}
POST /api/plugins/install {"source_repo_id":  1 }   -> 200 in 0.385s

The marketplace card embeds the repository id in an HTML attribute
(installPlugin('fnack.x', '1.0.0', '1')), so it reaches fetch() as a numeric string, while PluginRegistry compares it against integer repo ids — the lookup misses, and the install is refused before any network call. Every Install / Update / Switch click in the Marketplace was affected; only API/M2M callers sending an int worked. The deployed image serves the buggy line (curl -s localhost:4688/static/app.js | grep source_repo_id → const src = e.source_repo_id ? \, '${e.source_repo_id}'` : '';`).

Fix

  • static/app.js: emit the repository id as an unquoted number in the card action, and coerce in installPlugin() before sending.
  • plugins/api.py: accept a numeric string for source_repo_id (defensive for any client), leaving genuinely invalid values to the registry's real message.
  • plugins/api.py: log the refusal reason on install/update (and log successful installs with their source repo). The access log only recorded 400 <bytes>, which made this failure impossible to diagnose after the fact.

Regression test

tests/architecture/test_marketplace_repo_identity.py::test_install_api_accepts_string_and_int_repo_ids — string provenance accepted, int accepted, unknown repository still refused with the real reason, ambiguity still refused when no provenance is given.

Verification

  • Architecture suite 27/27 pass; smoke test passes.
  • API-level test exercises the real blueprint with string/int/unknown/ambiguous provenance.

Note

The exact 406-byte body of the original failure is not recoverable (gunicorn logs status + size only). The reproduction above is the deterministic cause for UI clicks; with the new logging, any future refusal prints its reason.

…wser installs were refused)

Reproduced against the live container — the marketplace UI's provenance value
is refused before any network call:

  POST /api/plugins/install {"source_repo_id": "1"}  -> 400 in ~0.004s
    {"error":"… is not published by repository 1 — it is published by: Official fnack Plugins (id 1)"}
  POST /api/plugins/install {"source_repo_id":  1 }  -> 200 in ~0.39s

The card action embeds the id in an HTML attribute (`installPlugin('id',
'ver', '1')`), so it reaches fetch() as a numeric STRING while
PluginRegistry compares it against integer repository ids — the lookup
misses and the install is refused. Every Install / Update / Switch click in
the Marketplace was affected; only M2M/API calls with an int worked.

- static/app.js: emit the repository id as an unquoted number in the card
  action and coerce in installPlugin() before sending it.
- plugins/api.py: accept a numeric string for source_repo_id (defensive, any
  client), leaving genuinely invalid values to the registry's real message.
- plugins/api.py: log the refusal reason on install/update, and log
  successful installs with their source repo. The gunicorn access log only
  recorded "400 <bytes>", which made a failed install impossible to diagnose
  after the fact (exactly what happened here).
- tests/architecture/test_marketplace_repo_identity.py: new API-level
  regression test — string provenance accepted, int provenance accepted,
  unknown repository refused with the real reason, ambiguity still refused
  when no provenance is given.
@tajanthind
tajanthind merged commit cb88585 into main Sep 10, 2026
2 checks passed
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