7436 Commits
Author SHA1 Message Date
András Marczinkó 64ce16db86 Added the missing semicolons to the grid-area manage-labels and the manage-share (#9298)
It added 4 missing semicolons to the base-theme frss.css files.
2026-09-12 22:20:59 +02:00
Morax cc45db5ef8 Fix adaptive reading state after marking articles read (#9290)
Closes #9288

Changes proposed in this pull request:

The mark-as-read links contain the resolved state, so the automatic reading preference becomes an explicit `state=2` filter after the redirect. Once the last unread article is marked read, the page stays empty instead of showing all articles.

- Keep the requested state separately from the resolved state used by the action. A redirect state of `0` reapplies the reading preference; explicit filters still survive the redirect as intended by #9007.
- Carry that value through the menu, footer and “Load more” link. This matters with oldest-first pagination, where the last footer supplies the menu button's action.
- Add request-state and rendered-pagination tests, plus a changelog entry.

How to test the feature manually:

1. With both read and unread articles, choose “Show unreads if any, all articles otherwise”. Mark all as read from the footer, then repeat using the top button. The resulting page should show the read articles.
2. Set one article per page and oldest-first sorting. Load the remaining articles before marking all as read; the same fallback should work.
3. Select an explicit unread filter: marking everything read should still leave an empty unread view. Also check a favourite filter with a search term, including “Mark selection unread”; the search and favourite scope should remain intact.

Validation: `make NO_DOCKER=1 fix-all` and `make NO_DOCKER=1 test-all` pass with PHP 8.5.9 (719 PHPUnit tests, 1,378 assertions). Browser checks passed against SQLite in Chromium (desktop and mobile viewport) and Firefox. The original footer fails three of the six new rendered-pagination cases.
2026-09-11 08:18:09 +02:00
Alexandre Alapetite 61d7925af1 Config + increase default values for search max length and depth (#9280)
Fix https://github.com/FreshRSS/FreshRSS/discussions/9279
Follow-up of https://github.com/FreshRSS/FreshRSS/pull/9277

Values can be overridden in `data/config.php`, or in `./data/config.custom.php` before the install process
2026-09-10 22:14:55 +02:00
Alexandre Alapetite d58459a84a Simplify pathInfo() (#9287)
Simplification of https://github.com/FreshRSS/FreshRSS/pull/9282
https://github.com/FreshRSS/FreshRSS/issues/9281#issuecomment-5618617227
2026-09-10 15:37:31 +02:00
InverleandAlexandre Alapetite 2cabd29d1b Fix infinite redirect loop due to SCRIPT_NAME in PATH_INFO (#9282)
* Fix infinite redirect loop due to `SCRIPT_NAME` in `PATH_INFO`

* Move to Minz_Request::pathInfo()

---------

Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-09-10 14:20:35 +02:00
Alexandre Alapetite a1e96a4f69 Small fixes changelog, credits 2026-09-09 17:01:28 +02:00
Alexandre Alapetite 11d56601ad Start next version 1.30.1-dev 2026-09-09 17:01:06 +02:00
Alexandre Alapetite 62eb3b405e Release 1.30.0 1.30.0 2026-09-09 16:35:42 +02:00
Alexandre Alapetite c32b5defc4 Changelog 2026-09-09 16:16:38 +02:00
AdamKorczandAlexandre Alapetite dc5cf8221b Limit the length and parentheses nesting depth of a search query (#9277)
* Limit the length and parentheses nesting depth of a search query.

* Use exception

---------

Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-09-09 16:12:41 +02:00
Ali GündoğarandAlexandre Alapetite 970b135190 Ignore security-relevant OPML attributes for dynamic OPML sources (#9276)
* Ignore security-relevant OPML attributes for dynamic OPML sources

OPML content can come from an untrusted source (a dynamic OPML category re-fetches its remote content on every refresh cycle). Such content must not be able to configure feed cURL parameters (CURLOPT_COOKIE, CURLOPT_PROXY, CURLOPT_POSTFIELDS, etc.) nor create further dynamic OPML categories. importOpml() therefore defaults to untrusted, and the local file import controller explicitly opts in.

* Review

---------

Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-09-09 16:11:38 +02:00
Inverle 7b6ec0f8d6 Log invalid challenge and ignore JSON error during login (#9278)
* Log and block invalid challenge formats during login

Fixes some unexpected behaviors during a user login, including strange errors in the log related to JSON.

* Ignore JSON decode errors for URL unserializing

* Document the format a bit
2026-09-09 16:11:05 +02:00
Alexandre Alapetite 54b687b7a1 Add missing contributors to credits 2026-09-09 00:51:51 +02:00
Alexandre Alapetite 296084793b Changelog 2026-09-09 00:17:23 +02:00
Ali GündoğarandAlexandre Alapetite 18dfada959 Only allow http/https URI schemes for enclosure and thumbnail URLs (#9272)
* Only allow http/https URI schemes for enclosure and thumbnail URLs

Feed enclosure URLs were stored and rendered without URI scheme validation:
SimplePie's scheme check only applies to URLs inside content HTML
(Sanitize::replace_urls()), not to enclosure metadata, so a `javascript:`
URL survived the whole pipeline and was served as a clickable link in the
article view, including through the Google Reader compatible API.

Add FreshRSS_http_Util::isAllowedUrlScheme() and apply it:
- at store time, when building enclosure attributes in FreshRSS_Feed
- at render time, in FreshRSS_Entry::content(), so entries already stored
  in existing databases are protected as well

https://github.com/FreshRSS/FreshRSS/security/advisories/GHSA-3fw6-j8m4-82vj

* Reuse existing SimplePie is_remote_uri() function

* Use more realistic test examples

* Add tests for protocol relative
And simplify tests further

---------

Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-09-09 00:14:13 +02:00
InverleandAlexandre Alapetite 58e32add92 SimplePie: Harden cache deserialization calls (#9273)
* SimplePie: Harden cache deserialization calls

To reduce impact if a maliciously provided cache file is somehow loaded, e.g. from a manipulated backup or some kind of external arbitrary file write vulnerability.

* Sync our SimplePie

https://github.com/FreshRSS/simplepie/pull/90

---------

Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-09-08 23:36:59 +02:00
Alexandre AlapetiteandInverle cea88ace69 Reword explanations for edge/latest channels (#9270)
* Reword explanations for edge/latest channels
Highlight the fact that we do not backport bug fixes or security fixes to previous releases.
Simplify also a slightly obsolte MySQL doc to favour SQLite

* Merge i18n: az

* Update Polish translations to specify rolling/versioned releases

Co-authored-by: Inverle <inverle@proton.me>

* Update 05_Release_new_version.md

---------

Co-authored-by: Inverle <inverle@proton.me>
2026-09-08 23:25:18 +02:00
Jamal Ali a169035755 Add Jamal Ali to CREDITS (#9271)
Adding my line to `CREDITS.md`, as asked in #9269 after the Azerbaijani translation was merged.
2026-09-08 17:35:51 +02:00
Jamal Ali 65e402ca41 i18n: add Azerbaijani translation (#9269) 2026-09-08 14:42:42 +02:00
Inverle c4b96b253c Fix auth CSRFs (login and register actions) (#9171)
This is done by getting rid of the CSRF check allowlist, and instead of refreshing the CSRF token on every unauthenticated page load, only refreshing the token when needed (on login, logout, account delete, HTTP auth user change)

In addition, the actualize actions are now correctly protected as well.

The most notable part is that the user create action could potentially be exploited by another website for XSS on a given instance, by submitting the register form with known credentials, and then by the attacker logging into the same account forcibly registered by a malicious website, and setting custom User JS code.
Login action was likely not exploitable in any meaningful way, because of the JS nonce.

It's worth noting one risk of merging this PR, some vulnerabilities discovered in the past had reduced impact due to CSRF token refreshing on every page load: https://github.com/FreshRSS/FreshRSS/security/advisories/GHSA-jf4v-f8p2-8xvq#:~:text=It%27s%20important,FreshRSS%5FAuth%3A%3AremoveAccess%28%29%2E
2026-09-08 09:19:01 +02:00
Inverle 5a842b25ee Format unread counter in title on page load (#9263)
For consistency with JS: https://github.com/FreshRSS/FreshRSS/pull/8577#issuecomment-4017150896
2026-09-06 21:36:12 +02:00
Inverle b182fb94e8 Fix desync between favicon and title unread counters (#9262)
Closes #8237
Regression from https://github.com/FreshRSS/FreshRSS/commit/80e7e15e3f619408db1752a3899ff708bc7bcc93, https://github.com/FreshRSS/FreshRSS/commit/d289c5340f3b21f1561e57390e89b0b5c7881d17

The function for updating the favicon count would only get called if there were articles newly marked as unread, so if there were only articles newly marked as read, the separate counters would display different values.

Behavior is now the same as for updating the title - always updating the favicon counter, without requiring any conditions to be true.
2026-09-06 21:35:15 +02:00
András Marczinkó 1a33a2999f Fix typo in alert error border color variable (#9266) 2026-09-06 21:33:40 +02:00
András Marczinkó 99ee090236 Fix PHP Warning: Cannot modify header information headers already sent (#9267)
Closes #9229

I restored my accidently deleted patches with @Inverle fixes. This fix is still works perfectly. Sorry. (https://github.com/FreshRSS/FreshRSS/pull/9230)

How to test the feature manually:

With this change:
1. Go to FreshRSS UserCSS extension config
2. Enable the UserCSS and set the CSS code in it.
3. Go to FreshRSS feeds page.
4. Hit the ctrl + F5 button and see no errors in the nginx log.
2026-09-06 21:32:48 +02:00
SONG ZHIFEI ce6e57ae0f Add XiaoSong1223 to contributors (#9268)
https://github.com/FreshRSS/FreshRSS/pull/9264
2026-09-06 21:29:41 +02:00
Alexandre Alapetite 40b3a7592a Changelog until 2026-08-16 2026-09-06 21:28:33 +02:00
Song ZhifeiandAlexandre Alapetite 148c140843 Fix marking filtered label articles as read in SQLite and PostgreSQL (#9264)
* Fix marking filtered label articles as read in SQLite

Remove the undefined entry alias from SQLite markReadTag filters. Add SQLite regression coverage for search and state filters, label boundaries, the maximum entry ID, and unread cache updates.

Fixes #9214

* Test label read filters across supported databases

Run the entry DAO regression cases against configurable SQLite, PostgreSQL, MySQL, or MariaDB connections using temporary tables. Match compressed content storage on MySQL/MariaDB, cover marking articles unread, and document how to run each backend.

* Keep only fix
Will rework test approach in future work

---------

Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-09-06 17:44:14 +02:00
dependabot[bot] e5f9906781 chore(deps): bump fast-uri from 3.1.5 to 3.1.7 (#9257)
Bumps [fast-uri](https://github.com/fastify/fast-uri) from 3.1.5 to 3.1.7.
- [Release notes](https://github.com/fastify/fast-uri/releases)
- [Commits](https://github.com/fastify/fast-uri/compare/v3.1.5...v3.1.7)

---
updated-dependencies:
- dependency-name: fast-uri
  dependency-version: 3.1.7
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-03 08:57:38 +02:00
dependabot[bot] c83e01f03c chore(deps-dev): bump eslint from 10.8.1 to 10.9.1 in the eslint group (#9253)
Bumps the eslint group with 1 update: [eslint](https://github.com/eslint/eslint).


Updates `eslint` from 10.8.1 to 10.9.1
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/compare/v10.8.1...v10.9.1)

---
updated-dependencies:
- dependency-name: eslint
  dependency-version: 10.9.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: eslint
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-01 21:33:14 +02:00
dependabot[bot] a8b722eee6 chore(deps-dev): bump phpstan/phpstan from 2.2.8 to 2.2.9 (#9254)
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan-phar-composer-source) from 2.2.8 to 2.2.9.
- [Commits](https://github.com/phpstan/phpstan-phar-composer-source/commits)

---
updated-dependencies:
- dependency-name: phpstan/phpstan
  dependency-version: 2.2.9
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-01 21:33:00 +02:00
dependabot[bot] 13703ded5c chore(deps): bump docker/setup-buildx-action from 4.2.0 to 4.3.0 (#9255)
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 4.2.0 to 4.3.0.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/bb05f3f5519dd87d3ba754cc423b652a5edd6d2c...37fe631027851001ddb9b187196cc803df7f5f0e)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-version: 4.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-01 21:32:29 +02:00
SamCyber001 7a23b44a61 Add SamCyber001 to CREDITS.md (#9250)
Contributions: #9246, #9248
2026-09-01 09:35:21 +02:00
SamCyber001 12b5be6afc Gate greader/json share formats behind Share by OPML (#9248)
* Gate greader/json share formats behind Share by OPML

Follow-up to #9246, per @Alkarex's suggestion on GHSA-rpmm-h4hx-5p6p.

The greader and json share formats expose origin.feedUrl (the feed's subscription
URL). Sharing feed URLs is what the "Share by OPML" option explicitly means, whereas
"Share by RSS" is meant to share article content. This moves greader and json in
p/api/query.php from the shareRss gate to the shareOpml gate, so feed URLs are only
served when the owner opted into OPML sharing.

Note: moved json alongside greader too, since both emit feedUrl (same toGReader path).

* sharedUrlGreader: gate on shareOpml

* Move greader share link under Share by OPML group
2026-08-31 14:43:55 +02:00
SamCyber001 14bf5f8c96 Strip feed URL credentials from anonymous query.php output (#9246)
p/api/query.php serialized entries with mode 'freshrss', putting the full stored
feed URL (including any user:pass@ credentials) into origin.feedUrl for f=json and
f=greader, exposing them to anonymous recipients of a "Share by RSS" link.

Call Feed::url(includeCredentials: false) on this path to strip the credentials,
matching the logging and OPML export paths.

Refs GHSA-rpmm-h4hx-5p6p
2026-08-30 19:25:06 +02:00
ci 1e33857256 i18n(es): complete Spanish translations (#9245)
- Reviewed all remaining Spanish translations marked as TODO or DIRTY
- Updated translations against the current English source
- Removed obsolete TODO, DIRTY, and IGNORE markers after verification
- Brought Spanish translation completion to 100%
2026-08-30 16:53:47 +02:00
ci 518cc9d890 docs: add payaci to contributors list (#9244)
Add my contributor entry to `CREDITS.md` following my merged contribution in #9242.
2026-08-29 23:43:28 +02:00
Inverle 0a44b7d017 Follow-up #9169 remove form IDs everywhere (#9205)
* Follow-up #9169 remove form IDs everywhere

* Add some `<form name>` alongside `class`
2026-08-29 23:21:39 +02:00
ci bed26d2c93 fix(i18n): resolve pt-BR dirty translations (#9242)
- Reviewed all remaining pt-BR translations marked as DIRTY
- Updated or validated translations against the current English source
- Removed obsolete DIRTY markers after verification
2026-08-29 22:38:52 +02:00
ci 5e625d638c fix(i18n): translate missing pt-BR strings (#9240)
- Translate missing pt-BR strings
- Remove resolved TODO markers
- Improve Brazilian Portuguese localization consistency
2026-08-29 22:28:22 +02:00
Alexandre Alapetite a6a27cb1fe Document CLI example to count all feeds (#9238)
* Document CLI example to count all feeds
https://framapiaf.org/@freshrss/117171837151951502
2026-08-29 22:15:48 +02:00
Alexandre Alapetite f902047204 Offer to add a prefix when resharing user labels (#9236)
* Offer to add a prefix when resharing user labels
Improve https://github.com/FreshRSS/FreshRSS/pull/8112

* make fix-all
2026-08-28 08:51:57 +02:00
07c939d84f Fix: infinite recursion in sanitizeHTML() when maxLength truncation doesn't converge (#9043)
* Fix: infinite recursion in sanitizeHTML() when maxLength truncation doesn't converge

Sanitizing can grow a truncated HTML fragment (e.g. an unclosed tag gets
auto-closed), so the previous recursive shrink-and-retry could hit a fixed
point and recurse forever, crashing on a stack overflow. Bound the retries
and fall back to a hard truncation that always terminates.

Add unit tests for FreshRSS_SimplePieCustom::sanitizeHTML(), the XSS
sanitization applied to all untrusted feed content, which had no coverage.

* Update app/Models/SimplePieCustom.php

Co-authored-by: Frans de Jonge <fransdejonge@gmail.com>

* Some refactoring
Avoid two mb_strcut in a row. More cases for fallback. Better fallback. More edge cases.

* Address trailing incomplete tag or entity

* Whitespace reduction

---------

Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
Co-authored-by: Frans de Jonge <fransdejonge@gmail.com>
2026-08-28 08:47:04 +02:00
Ali GündoğarandAlexandre Alapetite a9a5856d0d PostgreSQL: make commitNewEntries resilient to duplicate keys (#9231)
* PostgreSQL: make commitNewEntries resilient to duplicate keys

Use ON CONFLICT DO NOTHING

References #1610 #1614

* Minimize diff

* Minimize diff 2

---------

Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-08-26 12:20:16 +02:00
Luis Carlos SimitanaandAlexandre Alapetite 03e871e5be Add configurable log_level system setting (#9185)
* Add configurable log_level system setting

Minz_Log currently only distinguishes 'production' (errors and
warnings only) from any other environment (everything, including
debug messages), with no granularity in between.

Introduce an optional 'log_level' system setting (error, warning,
notice, info, or debug) that overrides the verbosity implied by
'environment'. It defaults to an empty string, which preserves the
exact current behaviour.

Fixes #7594

* Slight rewrite

---------

Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-08-25 16:18:30 +02:00
Alexandre Alapetite 2701b7dff4 Cleaner limit anonymous mode to default user (#9235)
While waiting for https://github.com/FreshRSS/FreshRSS/issues/1553
2026-08-25 16:17:36 +02:00
TowyTowyandClaude Opus 5 95842d81c1 Fix only the last <media:credit> being written to the RSS output (#9174)
The loop building the media credits assigned instead of appending, so an enclosure with several credits only kept the last one.

`app/views/index/rss.phtml` builds the `<media:credit>` elements for an enclosure in a loop, but assigns instead of appends:

```php
$mediaCredits = '';
foreach ($credits as $credit) {
    $mediaCredits = '<media:credit>' . $credit . '</media:credit>';
}
```

So an enclosure with three credits emits only the last one. Every iteration throws away what the previous one built.

This came in with #6272 ("Allow multiple authors on enclosures", which closed #5066). Parsing and display got multi-credit support, but FreshRSS's own generated RSS still passes on a single credit, so a downstream reader subscribing to a FreshRSS feed sees the same symptom #5066 described.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 22:49:52 +02:00
IEEE-754 3d59aeb476 i18n: Improve Traditional Chinese translations (#9223)
- Change the time format to use leading zeros
- Refine wording
- Add some new translations
2026-08-19 18:28:47 +02:00
InverleandAlexandre Alapetite a84c0a8bc2 Fix all broken links in the entire repository (#9207)
* Fix all broken links in the entire repository

* Update docs/CHANGELOG-old2.md

Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>

* Update config.default.php

Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>

* Update README.md

Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>

* Update docs/i18n/freshrss.fr.po

Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>

* Update docs/i18n/templates/freshrss.pot

Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>

* Update README.md

Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>

* Update docs/fr/users/01_Installation.md

Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>

* Update docs/fr/users/01_Installation.md

Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>

* Update docs/en/internationalization.md

Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>

* Manual fixes, preferences
* Prefer canonical when known, prefer shorter, prefer roots, prefer URLs with content negotation (e.g. language preference)
* Keep 302, 307 unchanged
* Use only `.example`, `example.net` or similarly reserved domains for URL examples

* Fix language negotiation for developer.mozilla.org

* Restore a Stackoverflow 302

* Fix some example.com

---------

Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-08-19 16:19:21 +02:00
Ihsan Arif adfea9092f i18n(id): complete Indonesian translation (#9220)
* i18n(id): complete Indonesian translation

Translate the 130 remaining `// TODO` entries in app/i18n/id/ and refresh
the 5 `// DIRTY` ones, bringing Indonesian from 88% to 100%.

Terminology follows the strings already shipped (umpan, kategori, artikel,
belum dibaca, tandai, pintasan). Placeholders, inline markup and
documentation links are preserved unchanged. Entries with no natural
Indonesian form are marked `// IGNORE` rather than left silently as
English: the `lt` language endonym, `gen.search.url`, and the API error
marker.

Replace `peladen` with `server` throughout, including in strings that
predate this change, as `peladen` is unfamle
doing so, fix a mistranslation where the "Browser" field of the system
information panel read `Peladen`, i.e. ser

All edits were made through cli/manipulateME
progress tables were regenerated with `composer run-script translations`.

Refs #6349.

* i18n(id): Fix Indonesian translations for update messages and sorting options
2026-08-19 15:24:56 +02:00
InverleandAlexandre Alapetite c7e6ab76fe Disallow fetching of non remote uri (#9215)
* Disallow redirection to non-remote URLs

Fix https://github.com/FreshRSS/FreshRSS/security/advisories/GHSA-fgq3-88jp-7rj9

* Disable unnecessary stream wrappers

* Add `is_remote_uri()` check at beginning of `httpGet()`

* SimplePie syntax

* Sync SimplePie
* https://github.com/FreshRSS/simplepie/pull/88

---------

Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-08-19 15:24:09 +02:00