Commit Graph
1004 Commits
Author SHA1 Message Date
Inverle 0addf32b36 Translate non-English code comments and identifiers (#9383)
Note: The changes in this PR are entirely AI-authored but manually reviewed and tested

## Summary

Translate the remaining French prose and French-derived identifiers in the main FreshRSS/Minz codebase to English.

Why: helps to understand the code

## Scope

- Minz comments, PHPDoc, and paginator identifiers
- Application comments and related identifiers
- Installer database identifiers
- Theme metadata descriptions and the identified CSS comments, previous discussion: https://github.com/FreshRSS/FreshRSS/pull/8241#discussion_r2553181653
- Related call sites and view helpers

External libraries and intentional i18n/docs content are excluded.
2026-10-20 09:52:31 +02:00
Inverle b33749fe6c New hook: Minz_HookType::AutofillField for auth views (#9362)
* New hook: `Minz_HookType::AutofillField` for auth views

Currently to be used in the Demo extension, because it overrides both the formLogin and reauth views (for autofilling username/password), it can be broken every time there is a slight change in core.
For example, after https://github.com/FreshRSS/FreshRSS/pull/9358.

In the future the hook can be extended to support more fields.

Demo extension PR: https://github.com/FreshRSS/xExtension-Demo/pull/6

* Suggested alternative without returning HTML code
2026-10-19 23:10:38 +02:00
tomac4tandInverle 859a2ad039 Fix local DNS leak when fetching feeds through a proxy (#9451)
* Fix local DNS leak when fetching feeds through a proxy

Follow-up to #9390: `getRetryAfterFile()` passed the full URL to
`Minz_Request::serverIsPublic()`, which then resolved the target hostname
with the local resolver (`dns_get_record()`), even when a proxy was
configured. With a socks5h/socks4a proxy this leaks the target hostname
to the local DNS and defeats remote name resolution.

- `Minz_Request::serverIsPublic()`: add an `$allowDnsResolve` parameter,
  and forward it to the recursive call so the guard holds for the whole
  recursion.
- `FreshRSS_http_Util::getRetryAfterFile()`: do not resolve the host name
  when a proxy is configured (`$proxy === ''`), since the proxy is
  responsible for DNS.

---------

Co-authored-by: Inverle <inverle@proton.me>
2026-10-17 17:37:27 +02:00
Alexandre Alapetite f617083328 Mitigate some 404 situations (#9435)
* Mitigate some 404 situations
Fix https://github.com/FreshRSS/FreshRSS/issues/9413 (maybe)
See https://www.php.net/function.session-regenerate-id

* Keep error_code in Session as well

* Ensure login_ok is false
2026-10-15 16:11:21 +02:00
0187b15c8e Sanitizer: block data: URI scheme while keeping data:image for images (#9417)
* Sanitizer: block data: URI scheme, keep data:image for images

FreshRSS_SimplePieCustom only disallowed the `javascript` scheme, so
`data:text/html,...` URIs in feed content survived
sanitization on <a href>, <iframe src>, <audio|video|source|track src>.
The default CSP neutralises these on FreshRSS pages, but clients and
deployments without that CSP (API WebViews, extensions using amendCsp(),
proxies stripping headers) could execute them. Same class as
GHSA-3fw6-j8m4-82vj / #9272.

- Sanitize::is_allowed_uri(): element/attribute-aware check that still
  permits inert `data:image/...` on img[src], image[src], video[poster]
  while blocking `data:` elsewhere; replace_urls() now uses it.
  is_allowed_scheme() is unchanged for backward compatibility.
- SimplePieCustom: disallow_uri_schemes(['javascript','data']).
- Tests: 4 blocked-input cases, a data:image keep-alive test and an
  `unsafe:` prefix test. They fail without the patch and pass with it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

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

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-10-11 20:36:09 +02:00
spa77k 9c04882cc1 Document how to use the User CSS and User JS core extensions (#9424)
Closes #9394

## Summary

The _User CSS_ and _User JS_ sections already existed in the user documentation, but they did not say where to find the settings. This explains that each extension has a gear icon (_Manage_) next to its switch, and gives the steps to use both extensions.

## Changes

- `docs/en/users/05_Configuration.md`: describe the extension list (switch, gear icon, name), add the steps for User CSS, and the same for User JS with a note that it might ask to sign in again.
- `docs/en/users/05_Configuration.md`, `docs/en/admins/11_Themes.md`, `docs/en/contributing.md`: the "CustomCSS extension" links pointed to FreshRSS/Extensions, where it is no longer available. They now point to the built-in User CSS section.
- `docs/en/admins/15_extensions.md`, `lib/core-extensions/README.md`: list the two core extensions with links to their documentation. Also fix the `.lib/core-extensions` path.
- `CREDITS.md`: add my name.

Only the English documentation is changed.
2026-10-09 23:04:22 +02:00
abp002andAlejandro Borrego 48547b4a6c Reduce memory consumption of export (#9350)
Contributes to #6139 (export part; the import will follow in another PR)

Exporting favourites was collecting the whole file in memory. With many articles, it fails with the default PHP memory limit.

## Changes
- New `Minz_View::helperToFile()`, similar to `helperToString()`, writing the output of a helper into a file by chunks of 64 KiB instead of keeping it in memory.
- `FreshRSS_Export_Service`: the `generate*()` methods now return the path of a temporary file instead of the content, and `zip()` builds the archive from those files (`ZipArchive::addFile()` instead of `addFromString()`). Temporary files are created in `TMP_PATH` (already required to be writable) and deleted at the end of the script.
- The export is sent with `readfile()`: Web UI, `cli/export-zip-for-user.php`, `cli/export-opml-for-user.php` and the OPML export of the Google Reader API.
- Note: this changes what `FreshRSS_Export_Service` returns (a path instead of the content). All callers in the repository are updated, and I could not find any public extension using this service.

## Measurements
Peak PHP memory (`memory_get_peak_usage(true)`), SQLite database with synthetic starred articles:

| Scenario | Before | After |
|---|---|---|
| Web, export your favourites, 10,000 articles (69 MB JSON) | 136 MB (fails with the default `memory_limit` of 128 MB) | 6 MB |
| Web, ZIP (OPML + favourites + labelled articles + 4 feeds), 10,000 articles | 152 MB | 6 MB |
| CLI `export-zip-for-user.php`, 10,000 articles | 189 MB (fails with 128 MB) | 8 MB |
| Web, export your favourites, 30,000 articles (208 MB JSON) | 404 MB | 10 MB |

Co-authored-by: Alejandro Borrego <210245044+abp002@users.noreply.github.com>
2026-10-05 21:47:19 +02:00
Alexandre Alapetite c7396a3a68 Update to GT\CssXPath 1.6.0 (#9411)
https://github.com/phpgt/CssXPath/releases/tag/v1.6.0
2026-10-05 21:44:57 +02:00
Inverle 4e5e142cd0 Remove vulnerable and unused code path for displaying errors (#9395)
If we were to remove or rename `errorController`, an HTML injection could potentially be introduced due to the fallback code path, that directly echoes the logs without any escaping.

(but mainly removing it to reduce the amount of code, and since it's unused)
2026-10-03 23:28:39 +02:00
Inverle 1f055da218 Avoid writing to log files during unit tests (#9373)
* Avoid writing to log files during unit tests

* Translate PHPDoc of `record()`

* Move environment variable into `phpunit.xml` and use tabs for indents
2026-09-30 21:50:47 +02:00
Inverle 05bfbb9f33 Move PHPUnit flags to a config file and use strictest failure mode (#9374)
See:
* https://docs.phpunit.de/en/12.5/xml-configuration-file.html#the-failonallissues-attribute
* https://docs.phpunit.de/en/12.5/xml-configuration-file.html#the-displaydetailsonallissues-attribute
2026-09-30 21:36:38 +02:00
5a270c02b0 Rotate session ID on all authenticated transitions (CWE-384) (#9333)
* auth: rotate session ID on all authenticated transitions (session fixation)

The PHP session ID was regenerated on explicit form login, reauth, logout and
password change, but NOT when access is (re)established from a remember-me
cookie, via HTTP authentication, or on self-registration auto-login (CWE-384),
so a fixed session ID could become authenticated.

Regenerate the session ID at those transitions too: in FreshRSS_Auth::init
when accessControl() first grants access (covers remember-me restore and HTTP
auth; skipped for auth_type 'none', which has no authentication boundary), and
in the self-registration auto-login path, mirroring the existing form-login
idiom. Also enable session.use_strict_mode before session_start as defence in
depth so an uninitialised, attacker-supplied session ID is not adopted.

* auth: avoid duplicate cookie during session rotation

* Code preferences, shorten some comments

---------

Co-authored-by: Inverle <inverle@proton.me>
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-09-28 10:39:21 +02:00
Inverle 6153087f68 Fix regression with HTTPS proxies due to wrong TLS SNI resolution (#9341)
Fixes #9314
More details: https://github.com/FreshRSS/FreshRSS/issues/9314#issuecomment-5750644306

Short version is that I forgot to consider CURLOPT_RESOLVE, since I was focused on making sure that socks4a and socks5h behaved correctly.

SimplePie fork PR: https://github.com/FreshRSS/simplepie/pull/91

(there would be problems only with some HTTPS proxies, such as the one described in the issue)
2026-09-26 15:54:16 +02:00
GigiandAlexandre Alapetite 6780afe846 Reject token access (RSS/OPML export, feed refresh) for disabled accounts (CWE-613) (#9336)
* auth: reject token access for disabled accounts (CWE-613)

Minz_Request::tokenIsOk() validated the per-user token but never checked that
the account is enabled, so a disabled user's token still authorised RSS/OPML
export and feed refresh (c=feed&a=actualize). Session-based access already
enforces `enabled` in FreshRSS_Auth::giveAccess(); apply the same check to
token validation. Re-enabling the account restores token access as expected,
so no token rotation is needed.

* Reduce comments

---------

Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-09-19 22:44:42 +02:00
Alexandre Alapetite 8067fd994b Use PHP SensitiveParameter (#9322)
https://www.php.net/class.sensitiveparameter
Active from PHP 8.2+ but parses without effect on PHP 8.1
2026-09-19 19:52:07 +02:00
Alexandre Alapetite ad4723cb1f Warn during session regenerate fail (#9311)
Might provide more logs for e.g. https://github.com/FreshRSS/FreshRSS/discussions/9305
2026-09-19 14:33:25 +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
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
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
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
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
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
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
Alexandre AlapetiteandFrans de Jonge 2ceb78fefb Dev updates: composer, hadolint, typos... (#9219)
* Dev updates: composer, hadolint, typos...
Composer compatibility PHP 8.6.
Manual updates not supported by Dependabot

* Fix hadolint

* Update lib/Minz/Url.php

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

* Add sha256sum for composer

---------

Co-authored-by: Frans de Jonge <fransdejonge@gmail.com>
2026-08-18 09:36:01 +02:00
0dc3431168 Preserve negative timezone offsets in date intervals (#9071)
* Preserve negative timezone offsets in date intervals

## Summary

- Preserve the sign of ISO 8601 timezone offsets while normalizing date intervals.
- Add regression coverage for a negative offset.

## Why

The previous normalization removed every hyphen, turning `-05:00` into `0500` and parsing the timestamp in the wrong timezone.

* Pad dates before timezone offsets

* Minor test comment and format

---------

Co-authored-by: Gerard Alvear <gerard.alvear@logiqd.me>
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-07-23 14:06:27 +02:00
InverleandAlexandre Alapetite 48abfd58fa Improve SSRF mitigations by restricting CURLOPT_PROXY (#8950)
* Improve SSRF mitigations by restricting `CURLOPT_PROXY`

Follow-up of https://github.com/FreshRSS/FreshRSS/pull/8400
SimplePie PR: https://github.com/FreshRSS/simplepie/pull/83

* Reword docs

* Change single quote to backquote in docs

* Fix `cURL error 5: Could not resolve proxy: Array` error

* Sync SimplePie

* A few fixes

* Implement suggestion

* A few more fixes

* Sync composer
https://github.com/FreshRSS/simplepie/pull/83

---------

Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-07-23 13:05:53 +02:00
InverleandAlexandre Alapetite f2e97b6399 Redirect away from wrong URL paths to fix displaying of relative URLs (#9075)
* Redirect away from wrong URL paths to fix displaying of relative URLs

* Also strip path info for API pages

* Error for API

---------

Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-07-22 19:07:01 +02:00
b12ec4fe77 Show only one representation per media:group (#9009)
* Show only one representation per media:group

A <media:group> gathers <media:content> elements that are effectively
the same content, yet different representations (e.g. several
resolutions of the same video), but every representation was stored and
rendered as its own enclosure. For instance, articles from PeerTube
feeds showed the same video five times.

When loading entries, keep only one <media:content> per <media:group>:
the one marked with isDefault="true", or the first one otherwise.
https://www.rssboard.org/media-rss#media-group

Fixes #8569

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Update SimplePie to include FreshRSS/simplepie#86

Regenerate the vendored SimplePie from FreshRSS/simplepie@freshrss via
`composer update --no-autoloader`, replacing the hand-patched Item.php
with the merged upstream version.

Includes <https://github.com/FreshRSS/simplepie/pull/86>.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Remove redundant test
Already covered by https://github.com/FreshRSS/simplepie/pull/86

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-07-20 18:27:57 +02:00
Frans de Jonge bffd20ba01 Simplepie: fix sanitizer whitelist stripping order (#9066)
* Simplepie: fix sanitizer whitelist stripping order

Includes <https://github.com/FreshRSS/simplepie/pull/85>.

Fixes <https://github.com/FreshRSS/FreshRSS/issues/9052>.

* composer update --no-autoloader
2026-07-19 11:50:56 +02:00
Gerard Alvear PorrasandGerard Alvear c15171efb5 Fix actualize mutex collision across instances (#9045)
## What changed

- derive the actualization mutex name from a SHA-256 hash of the canonical `DATA_PATH`
- keep `TMP_PATH` as the configurable location for the mutex file
- add coverage for deterministic per-instance names and independent locks

## Why

Multiple FreshRSS instances that use the default shared system temporary directory currently use the same actualization lock. The data path identifies the instance without exposing or depending on its configured salt.

## Validation

- `vendor/bin/phpunit --bootstrap ./tests/bootstrap.php ./tests/lib/ActualizeMutexTest.php --no-progress`
- `vendor/bin/phpcs app/actualize_script.php lib/lib_rss.php tests/lib/ActualizeMutexTest.php -s`
- `vendor/bin/phpstan analyse --memory-limit 512M --no-progress app/actualize_script.php lib/lib_rss.php tests/lib/ActualizeMutexTest.php`

Fixes #6370

Co-authored-by: Gerard Alvear <gerard.alvear@logiqd.me>
2026-07-18 22:32:38 +02:00
124dec5ef1 Check GMP during 32-bit installation (#9046)
* Check GMP during 32-bit installation

- add an installer prerequisite for GMP when PHP uses 32-bit integers
- stop installation before favourite export can call GMP functions that are unavailable
- cover 32-bit and 64-bit GMP requirement states

Google Reader-compatible entry identifiers require GMP on 32-bit PHP. The installer did not check that prerequisite, so a missing extension could instead cause a fatal error during favourite export.

* i18n: fr

---------

Co-authored-by: Gerard Alvear <gerard.alvear@logiqd.me>
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-07-18 21:54:36 +02:00
9f90d4f401 Warn during install if document root does not point to ./p (#9010)
* Warn during install if document root does not point to ./p

If the Web server's document root is misconfigured to serve the
FreshRSS repository root instead of the ./p public folder, other
folders such as ./data (which store credentials, tokens, and the
database) can become publicly accessible.

Add a non-blocking check to installation step 1 that compares the
detected DOCUMENT_ROOT against the ./p folder and warns when they
do not match. The check is skipped when DOCUMENT_ROOT is unknown
(e.g. CLI/cron context), including when it is an empty string,
since realpath('') would otherwise misleadingly resolve to the
current working directory.

Fixes #3679

* i18n: fr, en-US

* Path semantics preference

* Minor syntax preference

* Warn when document root is unavailable

---------

Co-authored-by: Gerard Alvear <gerard.alvear@logiqd.me>
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-07-18 20:07:00 +02:00
Gerard Alvear PorrasandGerard Alvear 5f94330503 Mailer: allow disabling SMTPAutoTLS via config (#9026)
PHPMailer enables SMTPAutoTLS by default, meaning it opportunistically
upgrades a plain SMTP connection to STARTTLS whenever the server
advertises support for it, regardless of the 'secure' => '' setting
documented in config.default.php. Minz_Mailer never read or set
$mail->SMTPAutoTLS, so this behaviour could not be turned off, making
it impossible to talk to SMTP servers with a self-signed or otherwise
untrusted certificate (e.g. a mail server on an internal Docker
network) without importing a custom CA.

Add a new 'auto_tls' key to the 'smtp' config array, defaulting to
true to preserve current behaviour, and wire it through to
$mail->SMTPAutoTLS in Minz_Mailer. Document the new option.

Fixes #2997

Co-authored-by: Gerard Alvear <gerard.alvear@logiqd.me>
2026-07-12 23:55:32 +02:00
TowyTowyandClaude Fable 5 f7c4fbeeed Redirect after saving user CSS/JS so changes apply immediately (#9006)
Saving custom CSS/JS rendered the response from the pre-save state (no
Post/Redirect/Get), so the stylesheet cache-busting URL was stale and
the change only appeared after a manual reload, which reads as erratic
behavior. Redirect back to the extension config after save, matching
the pattern used elsewhere in the codebase.

Fixes #8795

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 16:37:49 +02:00
Inverle 143cb09418 Minz: Skip loading JS/CSS asset if it's already in the list (#9000)
Fixes `Uncaught SyntaxError: redeclaration of const freshrssSliderLoadEvent` error shown in console on e.g. `/i/?c=subscription&id=1` due to https://github.com/FreshRSS/FreshRSS/pull/8973
2026-07-09 11:22:37 +02:00
Jam Balaya 310bcb5e90 Add read status hook for entries (#8995)
- Add a new `EntriesRead` / `entries_read` hook for extensions.
- Trigger the hook when `markRead()` changes entry read/unread state for explicit entry IDs.
- Cover both the default DAO path and the SQLite/PGSQL DAO path.
- Add Minz hook tests for the new hook signature and argument passing.

## Why

Extensions can already react to favorite/bookmark changes through `EntriesFavorite`. This adds the matching read/unread hook requested in #4051 for extensions that need to collect stats or react to manual read state changes.

Closes #4051
2026-07-07 09:04:32 +02:00
Inverle 9b43639c88 Fix slider not reinitializing after form submit (#8973)
"Follow-up" of https://github.com/FreshRSS/FreshRSS/pull/8612 (not fixing a regression, just a bug)

Some features like URL observers and custom favicon JS (e.g. *Reset to default* button) wouldn't work, due to `extra.js` not being loaded properly on pages where the slider content was already included in the HTML.
2026-07-04 12:14:05 +02:00
Alexandre Alapetite 339d5c7ef1 Minor Minz_Extension typing (#8952)
Declare some accessors as non-empty-string, to make some extension code slightly nicer, e.g. https://github.com/FreshRSS/Extensions/pull/470
2026-07-02 15:09:07 +02:00
InverleandAlexandre Alapetite dcec27c69d Add SSRF mitigations using filter_var and CURLOPT_RESOLVE (#8400)
* Add SSRF mitigations using `filter_var` and `CURLOPT_RESOLVE`
The idea is to prevent FreshRSS from sending any HTTP requests to internal services, except for the ones that are explicitly allowed in the config.

Based on https://github.com/moodle/moodle/blob/6e82b46a480826d1a85394d9e5087f7d82d1dd52/lib/filelib.php#L3818 and https://github.com/symfony/symfony/blob/8.1/src/Symfony/Component/HttpClient/NoPrivateNetworkHttpClient.php

https://github.com/FreshRSS/simplepie/pull/76
https://github.com/FreshRSS/simplepie/pull/78

* Add allowlist setting in Web UI

* make readme

* Update app/i18n/fr/admin.php

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

* make readme again

* make readme

* Further work

Still WIP and needs testing etc.

* Readd previous if check for domain combination allowlist

* Turn POST to GET after redirect

* Improve

* Update config.default.php

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

* make readme

* Skip SSRF check if `CURLOPT_PROXY` is set

* make readme

* Fix `!empty()` mistake

* Respect max redirects feed option when fetching with `httpGet()`

* Respect max redirects during SimplePie fetching + fix bypass

bypass fix: `CURLOPT_FOLLOWLOCATION` was moved below so that emulated redirects are enforced.

* Avoid FreshRSS and Minz code in SimplePie
https://github.com/FreshRSS/FreshRSS/pull/8400#discussion_r2935375980

* Corrected hook code

* phpdoc wrong return type

* Add CIDR support in allowlist

* Implement simple DNS caching

* Suppress `dns_get_record()` warnings

* A bit of proof-reading

* Minor typo

* Fix proxy logic

* Fix HTTP POST redirect logic

* Proofread checkCIDR
Add fixes for several situations

* Remove credentials from URL in logs

* Ensure `CURLOPT_FOLLOWLOCATION` is `false` by setting it at the end

* Fix codesniffer long line

* Fix potential bypass due to wrong return value

If there were no records returned by `dns_get_record()`, no overrides to `CURLOPT_RESOLVE` would get passed,
and a potential bypass could occur, when cURL would try to resolve the domain by itself.

* Put the URL at the end in logs

* Add documentation and environment variable support

* make readme

* Fix wrong behavior in case of IP

* Fix duplicate selector in CSS

* Minor type check change

* i18n fr, en

* Minor type check change

* Fix whitespace i18n fr

* make fix-all

* Fix `$ips_ok` not being returned after domain records were cached

* make readme

* PHPStan fix

* make readme

* Minor syntax in SimplePie

* Only return `null` if no allowed IPs were found

* Add wildcard *, help message

* Consistent docs with help message

* i18n: pl

* SimplePie compatibility PHP 7.2

* make fix-all

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

* 💥 Breaking change in the Changelog

* Document `INTERNAL_HOST_ALLOWLIST` in Docker docs

* Remove `Cookie` and `Authorization` headers in `httpGet()` during cross-origin redirect

* Minor whitespace
And same comment convention than below

* Remove authentication headers and change POST to GET on redirect in SimplePie

* Remove .local in Docker example

* Fill in default ports when comparing URL origins

* Remove .local from other places than the Docker example

* Rewrite WebSub subscribe to use `httpGet()`

* make fix-all

* Also unset `CURLOPT_USERPWD` during redirects

* phpcs fix

* Always unset `CURLOPT_FOLLOWLOCATION`

* Bump SimplePie
https://github.com/FreshRSS/simplepie/pull/78

* Update logic for CURLOPT_FOLLOWLOCATION

* Fix PHPStan

* Changelog fix security section

* Update most common RSS Bridge case
https://hub.docker.com/r/rssbridge/rss-bridge

* Replace misleading 127.0.0.1:8080 example for Docker
This does not make sense for a Docker container

---------

Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-06-28 18:51:04 +02:00
Alexandre Alapetite d9dc140a11 Minor bump SimplePie (#8947) 2026-06-27 22:14:25 +02:00
InverleandAlexandre Alapetite 1349c7c1a9 SimplePie: Disallow javascript: URI protocol (#8263)
Follow-up of https://github.com/FreshRSS/FreshRSS/pull/7924
https://github.com/FreshRSS/simplepie/pull/80
https://github.com/FreshRSS/simplepie/pull/65

* SimplePie: Disallow `javascript:` URI protocol

* Sync SimplePie

* Update code to work with SimplePie again

* Partial revert previous commit

* Bump SimplePie

---------

Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-06-13 21:15:36 +02:00
Alexandre Alapetite d40917b6d8 Bump SimplePie (fix PHP 8.5 bug) (#8918)
* https://github.com/simplepie/simplepie/pull/979
2026-06-11 22:16:33 +02:00
dependabot[bot]andAlexandre Alapetite 7d7e502603 chore(deps): update phpmailer/phpmailer requirement from 7.0.2 to 7.1.1 in /lib (#8907)
* chore(deps): update phpmailer/phpmailer requirement in /lib

Updates the requirements on [phpmailer/phpmailer](https://github.com/PHPMailer/PHPMailer) to permit the latest version.
- [Release notes](https://github.com/PHPMailer/PHPMailer/releases)
- [Changelog](https://github.com/PHPMailer/PHPMailer/blob/master/changelog.md)
- [Commits](https://github.com/PHPMailer/PHPMailer/compare/v7.0.2...v7.1.1)

---
updated-dependencies:
- dependency-name: phpmailer/phpmailer
  dependency-version: 7.1.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump PHPMailer
https://github.com/PHPMailer/PHPMailer/releases/tag/v7.1.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-06-03 13:06:28 +02:00
dependabot[bot]andAlexandre Alapetite f04788cb3a chore(deps-dev): bump phpstan/phpstan from 2.1.55 to 2.2.1 (#8906)
* chore(deps-dev): bump phpstan/phpstan from 2.1.55 to 2.2.1

---
updated-dependencies:
- dependency-name: phpstan/phpstan
  dependency-version: 2.2.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Fix bug in URL unserialize, json_decode

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-06-03 12:52:45 +02:00
Alexandre AlapetiteandKidd Lee a499778e12 SimplePie: XML_OPTION_PARSE_HUGE (#8894)
* https://github.com/FreshRSS/FreshRSS/pull/8710
* https://github.com/simplepie/simplepie/pull/977

Fix https://github.com/FreshRSS/FreshRSS/issues/8516

Co-authored-by: Kidd Lee <LeeXiaolan@users.noreply.github.com>
2026-05-26 22:28:59 +02:00
Alexandre Alapetite ff2add14a2 SimplePie: Fix wrong player_parent logic leading to invalid type (#8893)
```
PHP Fatal error: Uncaught TypeError:
SimplePie\Enclosure::__construct(): Argument #20 ($player) must be of type ?string, array given
in /opt/freshrss/lib/simplepie/simplepie/src/Enclosure.php:199
```

This happens when there is no`url` parameter.

Example of feed:
* https://feeds.feedburner.com/crunchyroll/rss/anime?lang=deDE

fix https://github.com/FreshRSS/FreshRSS/issues/8892

Upstream PR:
* https://github.com/simplepie/simplepie/pull/978
2026-05-26 22:16:01 +02:00
Julien HerrandAlexandre Alapetite 5bddc6bed5 feat(cli): add reconfigure-user.php to read/write per-user config attributes (#8873)
* feat(cli): add reconfigure-user.php to read/write per-user config attributes

Closes #8869.

Adds `cli/reconfigure-user.php`, a first-class CLI for per-user configuration attributes — the user-level equivalent of the existing `reconfigure.php` (system config).

### Usage

```sh
# List all attributes (sensitive keys redacted by default)
./cli/reconfigure-user.php --user alice --list
./cli/reconfigure-user.php --user alice --list --show-secrets

# Read a single attribute (exit 2 if key not found)
./cli/reconfigure-user.php --user alice --key language

# Set an attribute (type inferred from existing value: bool, int, string)
./cli/reconfigure-user.php --user alice --key language --set --value fr

# Set from stdin (recommended for secrets — keeps value out of shell history / ps)
./cli/reconfigure-user.php --user alice --key some_token --set --value-stdin < token.txt

# Create a new key, e.g. for an extension (unknown keys rejected by default)
./cli/reconfigure-user.php --user alice --key my_ext_setting --set --value hello --force

# Delete an attribute (exit 2 if key not found)
./cli/reconfigure-user.php --user alice --key some_token --unset
```

### Changes

- `cli/reconfigure-user.php` — new command
- `lib/Minz/Configuration::toArray()` — exposes the full config array (used by `--list`)
- `cli/README.md` — documents the new command
- `tests/cli/UserConfigOptionsParserTest.php` — PHPUnit tests for the options parser, following the existing `CliOptionsParserTest` pattern (shared `cli-parser-test.php` helper)

### Test plan

- `make test-all` passes
- Tested manually against a local FreshRSS instance: `--list`, `--key` (get), `--set` (bool/int/string inference), `--value-stdin`, `--unset`, `--force`, error paths (unknown key without `--force`, wrong type)

* unserialize allowed_classes

---------

Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-05-24 13:25:04 +02:00
Alexandre Alapetite 8e007af39d Fiix cookies with some browsers (#8867)
Fix https://github.com/FreshRSS/FreshRSS/issues/8850
Fix login with e.g. SeaMonkey
Regression due to https://github.com/FreshRSS/FreshRSS/pull/8778
2026-05-19 23:03:51 +02:00
Alexandre Alapetite 401fe12832 Sync lib_opml (no change) (#8853)
Follow-up of https://github.com/FreshRSS/FreshRSS/pull/8652
https://framagit.org/marienfressinaud/lib_opml/-/merge_requests/51
2026-05-17 19:20:53 +02:00