Skip to content

Commit c4bd979

Browse files
committed
Merge branch 'release/2.0.6'
2 parents 60c5cb7 + 6c3f616 commit c4bd979

8 files changed

Lines changed: 74 additions & 20 deletions

File tree

.htaccess

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,11 @@ RewriteRule .* index.php [L]
6060
RewriteRule ^(\.git|cache|bin|logs|backup|webserver-configs|tests)/(.*) error [F,NC]
6161
# Block all direct access to these sensitive user folders, whatever the file type
6262
RewriteRule ^(user)/(config|env)/(.*) error [F,NC]
63-
# Block user/accounts too, but allow avatar images (account://avatars) to be
64-
# served directly. Account data (.yaml password hashes) stays blocked; SVG is
65-
# excluded as a stored-XSS vector.
66-
RewriteCond %{REQUEST_URI} !/user/accounts/avatars/[^/]+\.(jpe?g|png|gif|webp|avif|bmp|ico)$ [NC]
63+
# Block user/accounts too, but allow avatar images to be served directly,
64+
# whether stored at user/accounts/avatars/<file> (flatfile accounts) or
65+
# user/accounts/<username>/<file> (Flex folder storage). Account data
66+
# (.yaml password hashes) stays blocked; SVG is excluded as a stored-XSS vector.
67+
RewriteCond %{REQUEST_URI} !/user/accounts/[^/]+/[^/]+\.(jpe?g|png|gif|webp|avif|bmp|ico)$ [NC]
6768
RewriteRule ^(user)/accounts/(.*) error [F,NC]
6869
# Block user/data too, but allow public asset uploads (e.g. Flex Object images)
6970
# to be served directly. SVG stays blocked as a stored-XSS vector; .css/.js are

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
# v2.0.6
2+
## 06/30/2026
3+
4+
1. [](#bugfix)
5+
* [security] Flex user avatars stored under `user/accounts/<username>/` (folder storage) are now served too; the 2.0.5 avatar carve-out only covered the flatfile `user/accounts/avatars/` layout, so folder-storage avatars kept returning a 403. Existing sites self-heal on upgrade. Fixes [getgrav/grav#4185](https://github.com/getgrav/grav/issues/4185).
6+
17
# v2.0.5
28
## 06/30/2026
39

system/defines.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
// Some standard defines
1111
define("GRAV", true);
12-
define("GRAV_VERSION", "2.0.5");
12+
define("GRAV_VERSION", "2.0.6");
1313
define("GRAV_SCHEMA", "1.8.0_2026-06-09_0");
1414
define("GRAV_TESTING", false);
1515

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
/**
4+
* Security postflight follow-up: widen the user/accounts avatar carve-out to
5+
* cover Flex folder-storage user avatars (getgrav/grav#4185).
6+
*
7+
* The 2026-06-30_0 update opened user/accounts only for the flatfile avatar
8+
* layout, user/accounts/avatars/<file> (the account://avatars stream). Flex
9+
* users on UserFolderStorage store their avatar media under the user object
10+
* folder instead, user/accounts/<username>/<file>, so those avatars kept
11+
* returning a 403.
12+
*
13+
* Both layouts are exactly two segments under user/accounts (<dir>/<file>), so
14+
* this widens the root .htaccess RewriteCond from the avatars-only path to any
15+
* two-segment image path. Account data (.yaml password hashes) is one segment
16+
* (user/accounts/<username>.yaml) or a non-image file inside the folder, so it
17+
* stays blocked. SVG stays blocked as a stored-XSS vector.
18+
*
19+
* The per-folder user/accounts/.htaccess backup already grants by <FilesMatch>
20+
* extension regardless of depth, so it needs no change. This is idempotent:
21+
* once the path is on the [^/]+/[^/]+ form the avatars-only literal is gone.
22+
*/
23+
24+
return [
25+
'preflight' => null,
26+
'postflight' =>
27+
function () {
28+
$root = GRAV_ROOT . '/.htaccess';
29+
if (is_file($root) && is_writable($root)) {
30+
$contents = file_get_contents($root);
31+
if ($contents !== false) {
32+
$patched = str_replace(
33+
'/user/accounts/avatars/[^/]+\.',
34+
'/user/accounts/[^/]+/[^/]+\.',
35+
$contents
36+
);
37+
if ($patched !== $contents) {
38+
@file_put_contents($root, $patched);
39+
}
40+
}
41+
}
42+
}
43+
];

webserver-configs/Caddyfile

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,13 @@ rewrite /(\.git|cache|bin|logs|backups|tests)/.* /403
1818
# deny all direct access to these sensitive user folders, whatever the file type
1919
rewrite /user/(config|env)/.* /403
2020

21-
# block user/accounts too, but allow avatar images (account://avatars) to be
22-
# served directly. SVG is intentionally excluded as a stored-XSS vector.
21+
# block user/accounts too, but allow avatar images to be served directly, whether
22+
# stored at user/accounts/avatars/<file> (flatfile accounts) or user/accounts/
23+
# <username>/<file> (Flex folder storage). SVG is excluded as a stored-XSS vector.
2324
# (Go's RE2 has no lookbehind, so this uses a negated matcher.)
2425
@user_accounts_nonavatar {
2526
path_regexp /user/accounts/.*
26-
not path_regexp (?i)/user/accounts/avatars/[^/]+\.(jpe?g|png|gif|webp|avif|bmp|ico)$
27+
not path_regexp (?i)/user/accounts/[^/]+/[^/]+\.(jpe?g|png|gif|webp|avif|bmp|ico)$
2728
}
2829
rewrite @user_accounts_nonavatar /403
2930

webserver-configs/htaccess.txt

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,11 @@ RewriteRule .* index.php [L]
6060
RewriteRule ^(\.git|cache|bin|logs|backup|webserver-configs|tests)/(.*) error [F,NC]
6161
# Block all direct access to these sensitive user folders, whatever the file type
6262
RewriteRule ^(user)/(config|env)/(.*) error [F,NC]
63-
# Block user/accounts too, but allow avatar images (account://avatars) to be
64-
# served directly. Account data (.yaml password hashes) stays blocked; SVG is
65-
# excluded as a stored-XSS vector.
66-
RewriteCond %{REQUEST_URI} !/user/accounts/avatars/[^/]+\.(jpe?g|png|gif|webp|avif|bmp|ico)$ [NC]
63+
# Block user/accounts too, but allow avatar images to be served directly,
64+
# whether stored at user/accounts/avatars/<file> (flatfile accounts) or
65+
# user/accounts/<username>/<file> (Flex folder storage). Account data
66+
# (.yaml password hashes) stays blocked; SVG is excluded as a stored-XSS vector.
67+
RewriteCond %{REQUEST_URI} !/user/accounts/[^/]+/[^/]+\.(jpe?g|png|gif|webp|avif|bmp|ico)$ [NC]
6768
RewriteRule ^(user)/accounts/(.*) error [F,NC]
6869
# Block user/data too, but allow public asset uploads (e.g. Flex Object images)
6970
# to be served directly. SVG stays blocked as a stored-XSS vector; .css/.js are

webserver-configs/nginx.conf

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,11 @@ server {
2121
location ~* /(\.git|cache|bin|logs|backup|tests)/.*$ { return 403; }
2222
# deny all direct access to these sensitive user folders, whatever the file type
2323
location ~* /user/(config|env)/.*$ { return 403; }
24-
# allow avatar images under user/accounts/avatars (account://avatars) to be
25-
# served directly; this must come before the user/accounts deny so it wins the
26-
# first-match. SVG is intentionally excluded as a stored-XSS vector.
27-
location ~* /user/accounts/avatars/[^/]+\.(jpe?g|png|gif|webp|avif|bmp|ico)$ { try_files $uri =404; }
24+
# allow avatar images under user/accounts to be served directly, whether stored
25+
# at user/accounts/avatars/<file> (flatfile accounts) or user/accounts/<username>/
26+
# <file> (Flex folder storage); this must come before the user/accounts deny so it
27+
# wins the first-match. SVG is intentionally excluded as a stored-XSS vector.
28+
location ~* /user/accounts/[^/]+/[^/]+\.(jpe?g|png|gif|webp|avif|bmp|ico)$ { try_files $uri =404; }
2829
# deny everything else under user/accounts, whatever the file type
2930
location ~* /user/accounts/.*$ { return 403; }
3031
# allow public media uploads under user/data (e.g. Flex Object images) to be

webserver-configs/web.config

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,14 @@
2929
<match url="^user/(config|env)/(.*)" ignoreCase="false" />
3030
<action type="Redirect" url="error" redirectType="Permanent" />
3131
</rule>
32-
<!-- Block user/accounts too, but allow avatar images (account://avatars)
33-
to be served directly. SVG is intentionally excluded as a
34-
stored-XSS vector. -->
32+
<!-- Block user/accounts too, but allow avatar images to be served
33+
directly, whether stored at user/accounts/avatars/<file> (flatfile
34+
accounts) or user/accounts/<username>/<file> (Flex folder storage).
35+
SVG is intentionally excluded as a stored-XSS vector. -->
3536
<rule name="user_accounts" stopProcessing="true">
3637
<match url="^user/accounts/(.*)" ignoreCase="false" />
3738
<conditions>
38-
<add input="{REQUEST_URI}" pattern="/user/accounts/avatars/[^/]+\.(jpe?g|png|gif|webp|avif|bmp|ico)$" negate="true" />
39+
<add input="{REQUEST_URI}" pattern="/user/accounts/[^/]+/[^/]+\.(jpe?g|png|gif|webp|avif|bmp|ico)$" negate="true" />
3940
</conditions>
4041
<action type="Redirect" url="error" redirectType="Permanent" />
4142
</rule>

0 commit comments

Comments
 (0)