Skip to content

Harden local HTTP backend and fix settings loss - #89

Open
DeMoN288 wants to merge 1 commit into
madoiscool:mainfrom
DeMoN288:fix/local-server-hardening-and-settings
Open

DeMoN288 wants to merge 1 commit into
madoiscool:mainfrom
DeMoN288:fix/local-server-hardening-and-settings

Conversation

@DeMoN288

Copy link
Copy Markdown

Summary

Local backend (127.0.0.1:6767) could be driven by any web page the user opens.

  • Routes ignored the HTTP method (MatchPost was MatchGet), so a plain GET reached state-changing routes. <img src="http://127.0.0.1:6767/remove/730"> on any site deleted that app's lua file, no CORS involved.
  • Access-Control-Allow-Origin: * with no Origin/Host check let any site call the other endpoints too, and nothing stopped DNS rebinding.

Changes in HttpServerService:

  • Every route matches on method and path (Match(method, expected, path, pattern, out long id)).
  • Requests are rejected with 403 unless Host is 127.0.0.1:6767 / localhost:6767 and Origin is absent (the app's own CefInjectorService HttpClient) or an https Steam surface (store.steampowered.com, steamcommunity.com, steamloopback.host).
  • CORS echoes the trusted origin instead of *.
  • Non-numeric / overflowing appids are a 404 instead of a 500 from long.Parse.
  • The netsh urlacl fallback reserves the URL for the current user instead of Everyone.

The plugin's existing calls are unaffected: CefInjectorService.CallBackendMethod already uses the correct method for every route and sends no Origin.

Settings silently lost. SettingsService.Save deletes settings.json when nothing is set, using a hand-kept list of fields. DnsMode was missing from it (the same bug the comment describes for FixesPageSize), so a user whose only change was the DNS mode lost it on the next save. The check is now derived from every AppSettings property. Save is also serialized (all saves share one .tmp path, so concurrent saves raced on File.Move), and IO failures are logged instead of thrown out of property setters.

PluginAddService: Busy was a read-then-write bool on the thread pool, so two quick picks could both start. It is now entered with Interlocked.CompareExchange.

CI: adds build-test.yml running dotnet build + dotnet test on windows-latest. Previously only the i18n check ran.

Testing

  • dotnet build clean, 0 errors.
  • New tests: HttpServerSecurityTests (trusted/untrusted Host+Origin, method mismatch, malformed ids) and SettingsEmptinessTests (every AppSettings property counts as non-empty — would have caught DnsMode). All 34 pass.
  • Full suite: 313/315 pass locally. The 2 failures (LuaVaultTests.Rename_*) are pre-existing and unrelated: they assert the English string "Build …" and fail on a machine with a non-English UI culture (they get "Сборка …"). They should pass on the CI runner.
  • Not tested: running the app end-to-end against a live Steam client.

Local backend (127.0.0.1:6767):
- Routes now match on HTTP method. A plain GET (e.g. <img src> on any
  web page, no CORS needed) could reach POST routes like /remove/{appid}.
- Reject requests whose Host is not 127.0.0.1/localhost:6767 (DNS
  rebinding) or whose Origin is not a Steam surface.
- CORS echoes the trusted origin instead of "*".
- Non-numeric appids are a 404 instead of a 500 from long.Parse.
- netsh urlacl is reserved for the current user, not Everyone.

Settings:
- The "nothing set, delete the file" check is now derived from every
  AppSettings property. DnsMode was missing from the hand-kept list, so a
  user whose only change was the DNS mode lost it on the next save.
- Save is serialized (all saves share one .tmp path) and IO failures are
  logged instead of thrown out of property setters.

PluginAddService: Busy is entered atomically, so two quick picks can no
longer both start a download.

Adds tests for all of the above and a CI workflow running build + test.
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