35 Commits

Author SHA1 Message Date
KodeStar 1c7ae3e335 Fix silent tile disappearance and ownership-reassignment bugs
Tiles could vanish without ever being deleted:

- Editing an item merged the editor's user_id into every save, so
  updating a visible item (e.g. a shared user_id=0 tile) silently
  reassigned ownership and hid it from everyone else. user_id is now
  set on create only, and excluded from update input in both Item and
  Tag controllers since it is mass-assignable.
- Deleting a user left their items orphaned with a dangling user_id,
  invisible to all users forever. The user's items are now hard-deleted
  with the account, and a data migration reassigns already-orphaned
  items to the admin user so previously "lost" tiles reappear.
- The Item global scope's ownership filter had an ungrouped orWhere,
  breaking operator precedence in any query that adds further clauses.

Includes regression coverage for ownership on create/update, user
deletion cleanup, and the orphan-recovery migration.
2026-07-09 10:52:29 +01:00
KodeStar 2555ab1b3c Run tests against in-memory sqlite and refuse real databases
The :memory: overrides in phpunit.xml had been commented out since 2024,
so RefreshDatabase ran migrate:fresh against the real .env database and
wiped it on every local test run. Enable the overrides, let the special
:memory: identifier bypass database_path() resolution and the boot-time
touch(), and add a TestCase guard that aborts the suite unless it is
pointed at in-memory sqlite.

ItemExportTest only ever passed by reading the populated dev database;
seed the root dashboard item it depends on so it passes on a fresh DB.
2026-07-09 10:52:17 +01:00
KodeStar 6f932918aa Fix import status regression from appload 404 + tighten upgrade tests
- itemImport: check response.ok in fetchAppDetails so a genuine 404
  (from the appload return-type fix) is reported as 'Failed to find app
  id' instead of being parsed as a successful import; applied to the
  source and the committed compiled bundle.
- phpunit.xml: point the schema URL at 12.5 to match the installed
  PHPUnit 12.5.x.
- ColorHelpersTest: exercise the get_brightness() non-hex stripping the
  test name promised (interior separators), which the prior assertion
  never covered.
2026-07-09 09:28:53 +01:00
KodeStar f1eec81591 Fix appload() returning a coerced HTTP 200 instead of a 404
ItemController::appload() was declared ': ?string', so its two error
branches that 'return response()->json([...], 404)' had the JsonResponse
coerced through Response::__toString() into a raw HTTP message served as
an HTTP 200 body. Widen the return type to
'\Illuminate\Http\JsonResponse|string|null' so those branches emit
real 404 JSON responses. The method body is unchanged, so the happy path
still returns the same JSON string and the frontend contract is preserved.

Flip the endpoint characterization test to assert the corrected 404.
2026-07-08 20:33:26 +01:00
KodeStar 46e09d172a Add upgrade-regression test coverage
Add tests guarding the surfaces most likely to break on a future
Laravel/PHP upgrade:
- Helper globals: format_bytes, parse_size, className, get_brightness,
  title_color (tests/Unit/helpers)
- CSRF exception config actually reaches the framework
  (PreventRequestForgery neverVerify list) and the excepted routes resolve
- Core GET routes boot and render on the current framework
- Filesystem disks resolve and the local disk root stays pinned to
  storage_path('app') (guards the Laravel 12 default-root change)
2026-07-08 20:33:25 +01:00
KodeStar 6d0242dead Merge pull request #1572 from linuxserver/feature/default-tag-group
Add a configurable default tag for the dashboard
2026-07-08 18:29:43 +01:00
KodeStar caa4f39edd Merge pull request #1567 from linuxserver/fix/export-import-tags
Include tags in item export and restore them on import
2026-07-08 18:29:18 +01:00
KodeStar a1f0d8f75d Merge pull request #1568 from linuxserver/fix/host-header-injection
Harden against host header injection and open redirect (CVE-2025-50578)
2026-07-08 18:29:04 +01:00
KodeStar ad9baffa62 Only offer pinned tags as the default tag
The default_tag dropdown was populated from every tag (type=1), but the
dashboard taglist only renders pinned tags. Selecting an unpinned tag as the
default therefore triggered a click on a taglist entry that does not exist,
silently doing nothing. Filter the option queries in both Setting accessors to
pinned tags so only selectable tags are offered, and assert an unpinned tag is
excluded.

Also drop the unused $data['default_tag'] assignment in ItemController: the
taglist partial reads the setting directly via Setting::fetch(), so the view
variable was never consumed.
2026-07-08 18:26:19 +01:00
KodeStar 9a9877a0dc Enforce TRUSTED_HOSTS allow-list regardless of APP_ENV
The custom TrustHosts middleware only overrode hosts(), so it inherited the
parent's shouldSpecifyTrustedHosts() gate, which skips enforcement whenever the
app runs in the local environment or under the test runner. Heimdall ships
APP_ENV=local by default (.env.example, copied to .env on install), so the
TRUSTED_HOSTS allow-list a user configures per the .env.example guidance was
never actually applied.

Override shouldSpecifyTrustedHosts() to tie enforcement to configuration
instead of environment: apply the allow-list whenever TRUSTED_HOSTS is set, in
any environment; when it is unset hosts() is empty and enforcement stays off,
preserving the historic no-restriction behaviour. Add handle()-driven tests
covering both the configured and unconfigured cases.
2026-07-08 18:22:49 +01:00
KodeStar c0c202c5ff Add a configurable default tag for the dashboard
In tags mode the dashboard always opened showing every link. This adds a
"Default tag" setting (Advanced) that pre-selects one tag group on load, so
the dashboard opens filtered to it - the built-in equivalent of the custom
JavaScript workaround people have been sharing.

The setting is a select populated from the user's own tags, following the
same dynamic-option pattern already used for the search provider. When a tag
is chosen its slug is exposed on the tag list and the matching tab is
activated on load; when the setting is empty, behaviour is unchanged and all
links are shown.

Resolves #1556
2026-07-08 17:10:19 +01:00
KodeStar f547ae42bb Return graceful output from get_stats instead of a 500
get_stats/{id} fataled when the item id was missing and 500'd whenever an
enhanced app's livestats() threw - a broken or updated remote app definition
(e.g. Komga) took the whole request down, and the frontend then stopped
refreshing that tile entirely.

getStats now returns valid JSON (200) with an inactive/empty payload when the
item is missing, has no class, references a stale class, or throws, logging
the failure for diagnosis. The successful path is unchanged and returns the
livestats output verbatim.

Resolves #1558
2026-07-08 15:21:39 +01:00
KodeStar 881533baa5 Harden against host header injection and open redirect
Heimdall trusted the incoming X-Forwarded-Host header for URL generation, so
a spoofed value poisoned the page base href, asset() URLs and redirect
targets - loading assets from and redirecting to an attacker-controlled host
(CVE-2025-50578).

- TrustProxies no longer trusts X-Forwarded-Host; a forged value can no longer
  influence getHost(), url(), asset() or redirects. X-Forwarded-For/Port/Proto
  handling is unchanged.
- Trusted proxies are now configurable via the TRUSTED_PROXIES env var
  (comma-separated CIDRs/IPs, "*" to trust all), defaulting to the previous
  private ranges.
- Added an opt-in TRUSTED_HOSTS allow-list: when set, only the listed hosts are
  served and any other Host header is rejected. Unset keeps the historic
  behaviour of serving arbitrary hosts, so existing installs are unaffected.

Resolves #1451
2026-07-08 14:42:12 +01:00
KodeStar fb9af1b216 Include tags in item export and restore them on import
The export endpoint (api/item) now emits each item's assigned tag titles,
excluding the root/default dashboard tag. On import, those titles are
resolved back to local tags - reusing an existing tag or creating a missing
one - instead of dropping every imported item onto the default dashboard.

Tags round-trip by title so a config can be moved between instances without
having to reassign each item to its section by hand.

Resolves #1555
2026-07-08 14:18:45 +01:00
Chris Hunt cd95fc3b92 Update search test 2025-08-02 17:43:49 +01:00
Chris Hunt 1e6b1f6de5 Add tests and fix user edit form 2025-07-15 17:04:47 +01:00
Chris Hunt 2df58472a1 Fix uploads and displaying of malicious SVG files 2025-07-13 17:00:23 +01:00
Shift f0ce9d633a Remove createApplication method 2025-07-10 18:54:07 +00:00
Shift 1fb6f75555 Set new ENV variables 2025-07-10 18:54:01 +00:00
Chris Hunt a0e5111771 Fix languages 2024-02-17 15:13:43 +00:00
Shift f3a5be79dc Remove redundant typing from DocBlocks 2024-02-16 21:13:14 +00:00
Shift 2cb837e4b5 Add type hints for Laravel 10 2024-02-16 21:13:13 +00:00
Attila Kerekes fbd050d4e4 fix: validate icons to be images (#1173) 2023-06-06 12:08:47 +02:00
Attila Kerekes 5d67f570a9 fix: validate icons to be images (#1167) 2023-06-05 18:27:30 +02:00
ullbergm 6e954a355d Add Trianglify (#1158) 2023-05-26 10:23:06 +02:00
Attila Kerekes a4022ce517 fix: Escape app title and tag title on list pages CVE-2022-47968 (#1088) 2023-01-05 20:31:15 +01:00
Attila Kerekes 9e6321e500 fix: Enable tag slug creation from CN characters (#1077) 2022-12-16 10:26:00 +01:00
Attila Kerekes 6b93f8ed5c test: Add item create test (#1058) 2022-12-05 16:28:18 +01:00
Attila Kerekes 45cc84c99c test: Add feature tests 2022-12-04 17:21:03 +00:00
Attila Kerekes bb5a078f35 feat: Add export import 2022-11-29 17:32:46 +00:00
Attila Kerekes 810ee03965 test: improve lang test debug output 2022-11-15 11:09:57 +01:00
Attila Kerekes edb51e56f1 feat: Dynamically add all languages to db
Add unit test to find missing translations
2022-11-14 19:31:07 +01:00
Kode 51ddaccc16 Update defaults 2022-03-19 15:04:26 +00:00
Shift b1dc4d4a41 Apply Laravel coding style
Shift automatically applies the Laravel coding style - which uses the PSR-2 coding style as a base with some minor additions.

You may customize the adopted coding style by adding a [PHP CS Fixer][1] or [PHP CodeSniffer][2] config to your project root. Feel free to use [Shift's Laravel ruleset][3] to help you get started.

For more information on customizing the code style applied by Shift, [watch this short video][4].

[1]: https://github.com/FriendsOfPHP/PHP-CS-Fixer
[2]: https://github.com/squizlabs/PHP_CodeSniffer
[3]: https://gist.github.com/laravel-shift/cab527923ed2a109dda047b97d53c200
[4]: https://laravelshift.com/videos/shift-code-style
2022-03-19 13:54:32 +00:00
Kode 3e1a7119d9 First commit 2018-01-26 14:35:01 +00:00