Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file removed archive/folderview.plus-2026.08.11.12.txz
Binary file not shown.
1 change: 0 additions & 1 deletion archive/folderview.plus-2026.08.11.12.txz.sha256

This file was deleted.

Binary file removed archive/folderview.plus-2026.08.19.07.txz
Binary file not shown.
1 change: 0 additions & 1 deletion archive/folderview.plus-2026.08.19.07.txz.sha256

This file was deleted.

Binary file added archive/folderview.plus-2026.08.20.01.txz
Binary file not shown.
1 change: 1 addition & 0 deletions archive/folderview.plus-2026.08.20.01.txz.sha256
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
66ef8fd9e6c7ff10de19560196e88ffe1c85e4e9434142aae2df17c8ef4dd6d1 folderview.plus-2026.08.20.01.txz
Binary file added archive/folderview.plus-2026.08.20.02.txz
Binary file not shown.
1 change: 1 addition & 0 deletions archive/folderview.plus-2026.08.20.02.txz.sha256
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
f0b2143536d393b8c641b9ac154795230aae5a3eb3ff319facbfc13bdb871b46 folderview.plus-2026.08.20.02.txz
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ This folder holds project documentation that does not need to stay at the reposi
- [TROUBLESHOOTING.md](TROUBLESHOOTING.md)
- [Content Security Policy readiness](security/CSP_READINESS.md)
- [Package trust and provenance](security/PACKAGE_TRUST.md)
- [Request security and reverse-proxy requirements](security/REQUEST_SECURITY.md)

## Architecture

Expand Down
35 changes: 35 additions & 0 deletions docs/TROUBLESHOOTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,41 @@ https://raw.githubusercontent.com/alexphillips-dev/FolderView-Plus/<commit>/fold

Do not edit `/boot/config/plugins/folderview.plus/version` to force an update. See [Installation and Upgrades](INSTALLATION_AND_UPGRADES.md#not-reinstalling-same-version).

### Reverse Proxy Actions Say `Blocked by request guard`

If pages load through SWAG or another TLS-terminating reverse proxy but create,
save, backup, restore, or other protected actions return HTTP 403, first test
the same action through the normal Unraid address. If direct access works, keep
the request guard enabled and verify the proxy authority headers.

For SWAG, include its standard proxy configuration in the Unraid location:

```nginx
include /config/nginx/proxy.conf;
proxy_pass http://<unraid-address>:<unraid-port>;
```

The resulting request must provide one value for each of these headers:

```nginx
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;
```

Do not add `X-FV-Request` in the proxy, remove `Origin` or `Referer`, or disable
the FolderView Plus guard. The plugin request client supplies its own request
marker. FolderView Plus rejects partial, repeated, malformed, or conflicting
forwarded values, including a forwarded hostname that differs from `Host`.

After changing SWAG, reload its Nginx configuration, hard-refresh the Unraid
page, and retry one protected action. If it still fails, export a sanitized
support bundle through the failing proxy URL. The
`system.requestSecurity` section reports privacy-safe origin and forwarded
authority reason codes without recording the hostname, IP address, or raw
header values. See [Request security and abuse controls](security/REQUEST_SECURITY.md#reverse-proxies).

### Import Fails Validation

Make sure Docker exports are imported into Docker and VM exports into VMs. Re-export with the latest plugin version if the file came from older tooling.
Expand Down
13 changes: 0 additions & 13 deletions docs/releases/2026.08.19.07.md

This file was deleted.

5 changes: 5 additions & 0 deletions docs/releases/2026.08.20.01.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
- Fix: Allow protected FolderView Plus actions through a correctly configured TLS-terminating reverse proxy by validating the external `Host`, `X-Forwarded-Host`, `X-Forwarded-Proto`, and `X-Forwarded-Port` authority as one coherent tuple.
- Security: Keep strict POST, request-marker, install-token, one-time nonce, transaction, same-origin, and rate-limit controls unchanged; reject partial, repeated, malformed, spoofed-host, and conflicting-port forwarded headers.
- Diagnostics: Add privacy-safe request-security reason codes to diagnostics and support bundles without recording hostnames, addresses, ports, or raw forwarded-header values.
- Docs: Add SWAG reverse-proxy requirements, a troubleshooting flow for `Blocked by request guard`, and guidance not to inject `X-FV-Request` or remove browser origin headers at the proxy.
- Test: Cover direct HTTP/HTTPS, standard SWAG forwarding, nonstandard ports, IPv6, spoofed and malformed tuples, and independent Origin/Referer disagreement.
5 changes: 5 additions & 0 deletions docs/releases/2026.08.20.02.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
- Fix: Allow protected FolderView Plus actions through a correctly configured TLS-terminating reverse proxy by validating the external host, protocol, and port as one coherent authority.
- Security: Keep strict POST, request-marker, install-token, one-time nonce, transaction, same-origin, and rate-limit controls while rejecting partial, repeated, malformed, spoofed-host, or conflicting forwarded headers.
- Diagnostics: Add privacy-safe request-security reason codes to diagnostics and support bundles without recording hostnames, addresses, ports, or raw forwarded-header values.
- Docs: Document supported SWAG forwarding, recovery from `Blocked by request guard`, and unsafe proxy workarounds to avoid.
- Test: Cover direct HTTP/HTTPS, standard and nonstandard proxy ports, IPv6, malformed or spoofed forwarded values, and Origin/Referer disagreements.
4 changes: 2 additions & 2 deletions docs/sbom.cdx.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"serialNumber": "urn:uuid:7d763a96-545a-896b-b7a7-d64cb56bf965",
"serialNumber": "urn:uuid:fc08839a-71b1-8d83-9a91-89ef86f15baa",
"version": 1,
"metadata": {
"component": {
"type": "application",
"name": "FolderView Plus",
"version": "2026.08.19.07",
"version": "2026.08.20.02",
"properties": [
{
"name": "folderview-plus:runtime-dependencies",
Expand Down
28 changes: 28 additions & 0 deletions docs/security/REQUEST_SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,34 @@ The browser request client performs nonce acquisition automatically. Mutation
retries remain disabled, so an uncertain response is reconciled from current
server state instead of replaying an operation.

### Reverse proxies

FolderView Plus supports a TLS-terminating reverse proxy without disabling its
same-origin guard. A proxied request can use its external authority only when
all of these headers are present as one coherent, single-valued tuple:

- `Host` and `X-Forwarded-Host` contain the same browser-facing hostname;
- `X-Forwarded-Proto` is exactly `http` or `https`;
- `X-Forwarded-Port` is a valid port from 1 through 65535; and
- an explicit port in `X-Forwarded-Host`, when present, equals
`X-Forwarded-Port`.

The request `Origin` and `Referer`, when supplied by the browser, must match
either the direct Unraid authority or that validated forwarded authority.
Partial, comma-separated, malformed, or conflicting forwarded headers are
ignored and the request fails closed when its origin does not match directly.
The forwarded headers never replace the request marker, install token, nonce,
transaction, POST, or rate-limit controls.

The standard LinuxServer SWAG `proxy.conf` supplies the required host,
forwarded-host, forwarded-protocol, and forwarded-port headers. A custom
location must retain those settings. It does not need to inject
`X-FV-Request`; the FolderView Plus browser client supplies its own marker.

Request-security diagnostics expose only bounded reason codes such as
`valid`, `partial`, `host-mismatch`, and `port-mismatch`. Hostnames, addresses,
and header values are not included in that diagnostic section.

The unload telemetry action is the only normal replay-protection exception. It
uses `sendBeacon`, cannot synchronously request a nonce, remains protected by the
install token and same-origin checks, and has a bounded high-volume telemetry
Expand Down
2 changes: 1 addition & 1 deletion docs/security/csp-readiness.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"mode": "report-only-shared-unraid-document",
"scope": {
"scannedRoot": "src/folderview.plus/usr/local/emhttp/plugins/folderview.plus",
"scannedFiles": 233,
"scannedFiles": 234,
"exclusions": [
"third-party icon asset pack",
"vendored scripts",
Expand Down
34 changes: 17 additions & 17 deletions folderview.plus.plg
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
<!ENTITY launch "Settings/FolderViewPlus">
<!ENTITY plugdir "/usr/local/emhttp/plugins/&name;">
<!ENTITY pluginURL "https://raw.githubusercontent.com/&github;/main/folderview.plus.plg">
<!ENTITY version "2026.08.19.07">
<!ENTITY md5 "037c51fa1ccba8d36ad248bf66ac4147">
<!ENTITY sha256 "f64322b8fda9127b3c192fea5c16d375e6965c4cec7481818daf5517dfdeab1f">
<!ENTITY version "2026.08.20.02">
<!ENTITY md5 "c58c8bb851e14cbdc2764b50cfbb322b">
<!ENTITY sha256 "f0b2143536d393b8c641b9ac154795230aae5a3eb3ff319facbfc13bdb871b46">
<!ENTITY iconPackVersion "1.0.0">
<!ENTITY iconPackMd5 "a149c36b41339949bb5c1eecad550704">
<!ENTITY iconPackSha256 "992f6c3544a8a3c1db80b861472fdd8b3d499f20f81796ed71405a10beb750bd">
Expand All @@ -18,20 +18,20 @@
<PLUGIN name="&name;" author="&author;" version="&version;" launch="&launch;" pluginURL="&pluginURL;" icon="folder-icon.png" support="https://forums.unraid.net/topic/197631-plugin-folderview-plus/" min="7.0.0">
<CHANGES>

###2026.08.19.07
- Fix: Eliminate the 5–7 second Docker-page refresh loop caused by API container-identity differences recursively scheduling native list reloads after FolderView renders.
- Fix: Prevent FolderView-generated images with blank or unreachable icon URLs from repeatedly requesting the failed source during subsequent FolderView renders, while immediately using the local fallback for blank values.
- Fix: Restore Docker folder boxes created through the Add Folder wizard when Unraid identifies container rows by short container IDs instead of container names.
- Fix: Keep populated folders visible when Hide empty folders removes earlier placeholders and later populated folders have high saved-order positions.
- Reliability: Keep Unraid's native Docker/PHP configuration revision authoritative for structural changes, use GraphQL only to enrich containers already present in the host runtime map, and preserve native rows during Docker busy-poll cycles.
- Reliability: Resolve Docker row identities through the normalized runtime model, retain deterministic first-folder ownership for overlapping rules, insert folder shells relative to connected host rows, and leave member rows under Unraid ownership if a shell cannot be attached safely.
- Performance: Replace broad action-followup redraws with incremental runtime reconciliation and remove duplicate deferred support-snapshot scheduling to avoid unnecessary request bundles, folder grouping, preview hydration, and DOM replacement.
- Diagnostics: Retain five bounded Docker-page session summaries with privacy-safe reload-source attribution, render and request counts, cadence, busy-cycle recovery, API-mismatch aggregates, and the `FVPLUS-DKR-REFRESH-001` healthy/suspected/confirmed verdict.
- Diagnostics: Count completed renders by monotonic generation, keep trace-health timestamps current, age browser-error evidence, and report aggregate host-row resolution, folder claims, missing members, hide-empty removals, and folder-shell insertion outcomes.
- Privacy: Keep runtime, refresh, API, icon, and folder-grouping diagnostics bounded and free of container identities, raw API payloads, GraphQL queries, URLs, tokens, cookies, call stacks, process lists, and syslogs.
- Docs: Expand troubleshooting and Docker runtime/API architecture guidance for native refresh ownership, busy polling, identity mismatches, folder grouping, support-bundle interpretation, and safe escalation.
- Test: Add coordinator, native busy-indicator, double-reload, image fallback, short-ID grouping, high-position hide-empty, repeated-render, browser-fixture, support-bundle, and large-runtime performance regressions.
- Quality: Validate the complete stable promotion through strict lint, static analysis, unit, workflow, release, install, deterministic-build, browser, theme, responsive, performance, dependency, Git-integrity, and GitHub security checks.
###2026.08.20.02
- Fix: Allow protected FolderView Plus actions through a correctly configured TLS-terminating reverse proxy by validating the external host, protocol, and port as one coherent authority.
- Security: Keep strict POST, request-marker, install-token, one-time nonce, transaction, same-origin, and rate-limit controls while rejecting partial, repeated, malformed, spoofed-host, or conflicting forwarded headers.
- Diagnostics: Add privacy-safe request-security reason codes to diagnostics and support bundles without recording hostnames, addresses, ports, or raw forwarded-header values.
- Docs: Document supported SWAG forwarding, recovery from `Blocked by request guard`, and unsafe proxy workarounds to avoid.
- Test: Cover direct HTTP/HTTPS, standard and nonstandard proxy ports, IPv6, malformed or spoofed forwarded values, and Origin/Referer disagreements.


###2026.08.20.01
- Fix: Allow protected FolderView Plus actions through a correctly configured TLS-terminating reverse proxy by validating the external `Host`, `X-Forwarded-Host`, `X-Forwarded-Proto`, and `X-Forwarded-Port` authority as one coherent tuple.
- Security: Keep strict POST, request-marker, install-token, one-time nonce, transaction, same-origin, and rate-limit controls unchanged; reject partial, repeated, malformed, spoofed-host, and conflicting-port forwarded headers.
- Diagnostics: Add privacy-safe request-security reason codes to diagnostics and support bundles without recording hostnames, addresses, ports, or raw forwarded-header values.
- Docs: Add SWAG reverse-proxy requirements, a troubleshooting flow for `Blocked by request guard`, and guidance not to inject `X-FV-Request` or remove browser origin headers at the proxy.
- Test: Cover direct HTTP/HTTPS, standard SWAG forwarding, nonstandard ports, IPv6, spoofed and malformed tuples, and independent Origin/Referer disagreement.


###2026.08.19.06
Expand Down
2 changes: 1 addition & 1 deletion folderview.plus.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<Description>
FolderView Plus organizes Docker, VM, and Dashboard views into folders in Unraid, with starter setup tools, rules, bulk assignment, recovery, templates, and diagnostics.
</Description>
<Date>2026-08-19</Date>
<Date>2026-08-20</Date>
<MinVer>7.0.0</MinVer>
<ExtraSearchTerms>folder view docker vm dashboard organization groups sorting import export backup recovery rules templates diagnostics</ExtraSearchTerms>
<Support>https://forums.unraid.net/topic/197631-plugin-folderview-plus/</Support>
Expand Down
9 changes: 1 addition & 8 deletions scripts/build_release_notes.sh
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ fi
OVERRIDE_FILE="docs/releases/${VERSION}.md"

append_provenance() {
local source_commit previous_ref compare_url archive_checksum total_commits
local source_commit previous_ref compare_url archive_checksum
source_commit="$(git rev-parse HEAD)"
previous_ref="${FVPLUS_RELEASE_PREVIOUS_REF:-$(git describe --tags --abbrev=0 --match 'v*' HEAD 2>/dev/null || true)}"
archive_checksum=""
Expand All @@ -69,13 +69,6 @@ append_provenance() {
# shellcheck disable=SC2016
printf -- '- Previous stable reference: `%s`\n' "${previous_ref}"
printf -- '- Full source comparison: %s\n' "${compare_url}"
printf '\n### Included commit history\n\n'
# shellcheck disable=SC2016
git log --no-merges --format='- `%h` %s' "${previous_ref}..HEAD" --max-count=75
total_commits="$(git rev-list --count --no-merges "${previous_ref}..HEAD")"
if (( total_commits > 75 )); then
printf -- '- …and %s earlier commit(s); use the full source comparison above.\n' "$((total_commits - 75))"
fi
else
printf -- '- Previous stable reference: unavailable\n'
fi
Expand Down
3 changes: 3 additions & 0 deletions scripts/release_notes_consistency_guard.sh
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ if (!rendered.includes(`## FolderView Plus ${version}`)) {
if (!rendered.includes('### Changes')) {
fail('Generated release notes are missing the changes heading.');
}
if (/^### Included commit history\s*$/m.test(rendered)) {
fail('Generated release notes must not include commit history.');
}
if (hasOverride) {
if (!overrideBody) {
fail(`Curated release override ${path.relative(root, overridePath)} is empty.`);
Expand Down
Loading