Skip to content

fix(api): answer 410 on every retired Companion route - #665

Merged
Valtora merged 1 commit into
mainfrom
fix/retired-companion-410
Oct 23, 2026
Merged

Valtora merged 1 commit into
mainfrom
fix/retired-companion-410

Conversation

@Valtora

@Valtora Valtora commented Oct 23, 2026

Copy link
Copy Markdown
Owner

Pull Request

Description

Eleven retired Companion routes answered 422 instead of the 410 Gone that docs/SECURITY.md promises. Their shared handler took **_: str, which FastAPI reads as a required query parameter named _, so only a call with ?_= reached it. The handler now takes no parameters, since it reads nothing.

A new test calls every method each retired route is registered for, the /companion/{legacy_path:path} prefix included, once bare and once with a query string and a JSON body. It checks the 410, the documented body and that no cookie is set. A second test checks that its table matches the routes on legacy_companion_router, so a route added or dropped there cannot go untested. A third checks that the retired paths stay out of the OpenAPI schema and that no _ parameter appears in it. On main, 14 of the 20 route and method cases fail with the 422; the six prefix cases pass.

Decision record

  • Fix the signature. I dropped the parameters rather than adding a catch-all dependency or middleware. The path parameters (request_id, recording_id) still match without being declared, and the handler never read them.
  • Methods stay as registered. A method a route was never registered for still gets 405, as before. Answering 410 to every method would widen the retired surface beyond what the old endpoints served, and the docs promise 410 for the endpoints, not for every verb.
  • Schema. Every retired route already had include_in_schema=False, so the _ parameter never reached the published OpenAPI schema. I kept them hidden, since a retired endpoint should not be advertised, and the new test pins that.
  • Left alone. system.py keeps its own copy of the 410 body for /system/companion-releases. Importing it from api.py would be circular, and both copies are checked against the same expected body in the tests.

Fixes: #664

Type of change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that changes existing behaviour)
  • Documentation update

Checks run

  • Python: scripts/check.py lint format blindexcept heldpins typecheck docs alembic and the full pytest run (3633 passed, with Postgres) in nojoin-dev-tests:local; check_whitespace.py, check_file_size.py, validate_docs.py and git diff --check on the host
  • Frontend: cd frontend && npm run lint && npm run check:contrast && npm run test && npm run build
  • Site: cd site && npm ci && npm run build
  • Docs only: python3 scripts/validate_docs.py

Migration impact

  • No database migration.
  • Adds an Alembic migration: one checked-in head, no committed revision deleted or renamed, upgrade and downgrade behaviour described above, and python3 scripts/check_migrations.py run against a throwaway Postgres.

Documentation impact

  • No documentation change required. docs/SECURITY.md already says these endpoints return 410; they now do.
  • Updated the guide that owns each changed behaviour, setup, deployment or support topic (.github/CONTRIBUTING.md, "Documentation Ownership").

Security impact

  • No security-sensitive change.
  • Touches auth, tokens, encryption, capture ownership or exposure; docs/SECURITY.md boundaries preserved and updated where behaviour changed.

The retired routes issued no credentials before or after; only the status code changes from 422 to 410.

Manual verification

Against the app in the test container: GET /api/v1/companion/ and /api/v1/companion/a/b/c?x=1 answer 410; GET on the POST-only /login/companion-pairing/disconnect answers 405, as before; a CORS preflight from a disallowed origin is refused by the CORS middleware, as before.

Screenshots (if relevant)

None.

The handler behind the eleven retired Companion routes took `**_: str`,
which FastAPI reads as a required query parameter named `_`. A call
without `?_=` got a 422 before the handler ran, so only the
`/companion/...` prefix gave the 410 Gone that docs/SECURITY.md
promises. The handler now takes no parameters. A test calls every
method each retired route is registered for, with and without a query
string and body, and checks the 410, its body and that no cookie is set.

Fixes: #664
@Valtora
Valtora merged commit 12aba5e into main Oct 23, 2026
21 checks passed
@Valtora
Valtora deleted the fix/retired-companion-410 branch October 23, 2026 18:32
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.

Retired Companion routes answer 422 instead of 410

1 participant