diff --git a/.nextcloudignore b/.nextcloudignore
index e8fb483cc47bb..e3b68170c3b68 100644
--- a/.nextcloudignore
+++ b/.nextcloudignore
@@ -54,6 +54,7 @@ window.d.ts
# Server specific
/.devcontainer
+/.oss-scanner
/__mocks__
/__tests__
/apps/testing
diff --git a/.oss-scanner/Dockerfile b/.oss-scanner/Dockerfile
new file mode 100644
index 0000000000000..431dcec927d0a
--- /dev/null
+++ b/.oss-scanner/Dockerfile
@@ -0,0 +1,49 @@
+FROM php:8.5-apache-trixie
+ENV DEBIAN_FRONTEND=noninteractive \
+ COMPOSER_NO_INTERACTION=1
+RUN apt-get update && apt-get install -y --no-install-recommends \
+ git unzip libfreetype6-dev libgmp-dev libicu-dev libjpeg-dev libldap2-dev libpng-dev libwebp-dev libzip-dev \
+ && rm -rf /var/lib/apt/lists/* \
+ && docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp \
+ && docker-php-ext-configure ldap --with-libdir="lib/$(dpkg-architecture --query DEB_BUILD_MULTIARCH)" \
+ && docker-php-ext-install -j "$(nproc)" bcmath exif gd gmp intl ldap pcntl zip \
+ && pecl install APCu-5.1.28 && docker-php-ext-enable apcu && rm -rf /tmp/pear
+COPY --from=composer:2 /usr/bin/composer /usr/local/bin/composer
+
+RUN cp "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" \
+ && printf 'memory_limit=512M\nopcache.enable=0\n' > "$PHP_INI_DIR/conf.d/nextcloud.ini" \
+ && a2enmod headers rewrite \
+ && sed -ri 's!/var/www/html!/src!g' /etc/apache2/sites-available/000-default.conf \
+ && printf '\n AllowOverride All\n Require all granted\n\n' > /etc/apache2/conf-available/nextcloud.conf \
+ && a2enconf nextcloud \
+ && printf '%s\n' '#!/bin/sh' 'exec setpriv --reuid=www-data --regid=www-data --init-groups env HOME=/tmp "$@"' > /usr/local/bin/as-www-data \
+ && printf '%s\n' '#!/bin/sh' 'exec as-www-data php /src/occ "$@"' > /usr/local/bin/occ \
+ && chmod +x /usr/local/bin/as-www-data /usr/local/bin/occ \
+ && git config --system --add safe.directory '*' \
+ && install -d -o www-data -g www-data /var/lib/nextcloud/data
+
+COPY --chown=www-data:www-data . /src
+WORKDIR /src
+
+RUN as-www-data git submodule update --init --depth 1 \
+ && as-www-data composer install
+
+RUN occ maintenance:install --database=sqlite --data-dir=/var/lib/nextcloud/data --admin-user=admin --admin-pass=admin \
+ && occ config:system:set appstoreenabled --type=boolean --value=false \
+ && occ app:enable admin_audit encryption files_external user_ldap \
+ && OC_PASS=alice occ user:add --password-from-env --group=alice alice \
+ && OC_PASS=bob occ user:add --password-from-env --group=bob bob \
+ && cp -a /var/lib/nextcloud/data /var/lib/nextcloud/data.pristine \
+ && cp -a config/config.php /var/lib/nextcloud/config.php.pristine \
+ && printf '%s\n' '#!/bin/sh' 'set -e' \
+ 'rm -rf /var/lib/nextcloud/data' \
+ 'cp -a /var/lib/nextcloud/data.pristine /var/lib/nextcloud/data' \
+ 'cp -a /var/lib/nextcloud/config.php.pristine /src/config/config.php' \
+ > /usr/local/bin/nextcloud-reset \
+ && chmod +x /usr/local/bin/nextcloud-reset
+
+RUN apachectl start && sleep 1 \
+ && (occ status --exit-code || echo "WARNING: occ status reported a problem") \
+ && (test "$(curl -s -o /dev/null -w '%{http_code}' http://localhost/config/config.php)" != 200 \
+ || echo "WARNING: config/config.php is served") \
+ && apachectl stop
diff --git a/.oss-scanner/Dockerfile.license b/.oss-scanner/Dockerfile.license
new file mode 100644
index 0000000000000..8c5ffd5249446
--- /dev/null
+++ b/.oss-scanner/Dockerfile.license
@@ -0,0 +1,2 @@
+SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
+SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/.oss-scanner/threat_model.md b/.oss-scanner/threat_model.md
new file mode 100644
index 0000000000000..a22662fd43bbf
--- /dev/null
+++ b/.oss-scanner/threat_model.md
@@ -0,0 +1,82 @@
+# Threat model
+
+## What this project does and where untrusted input enters
+Nextcloud is a self-hosted file sync and collaboration platform written in PHP.
+- Many users with different rights share one instance. Each user's files and data must stay within what they shared, and with whom.
+- Official threat model and accepted risks: https://nextcloud.com/security/threat-model/
+
+Untrusted input enters through:
+- Unauthenticated requests: login and password reset, public share links (`index.php/s/`, `public.php/dav`), OCS and status endpoints.
+- Authenticated users with no admin rights, via WebDAV (`remote.php/dav`), OCS (`ocs/v2.php`), and app controllers.
+- File content uploaded by users: previews, metadata extraction, and archive handling.
+- Federated servers (federated sharing, `ocm-provider`) and remote storages.
+
+## Principals and trust
+From least to most privileged:
+- **Anonymous visitor.** Can sign in, reset a password, and see what public endpoints expose.
+- **Share link holder.** Holds a public share token, sometimes with a password, sometimes upload-only ("file drop"). Must reach only the shared node, with only the permissions of the share, and only after the password check.
+- **Federated server.** Untrusted, including its administrators. Must reach only what was shared with its users.
+- **User.** Must stay within their own files and what others shared with them, at the permissions of the share. Reshares must not exceed the permissions of the share they came from.
+- **App password or scoped token.** Must not exceed the scope it was created with, e.g. one created without filesystem access must not read files.
+- **Admin.** Fully trusted, including group admins (subadmins) and delegated admins, and expected to be able to execute arbitrary code. The same goes for the server operator, `config/config.php`, the database, `occ`, and installed apps, which are not isolated from each other.
+
+## Components that matter most / least
+- Most important:
+ - authentication, sessions, and app passwords (`lib/private/Authentication`, `core/`)
+ - sharing and permission checks (`lib/private/Share20`, `apps/files_sharing`)
+ - WebDAV (`apps/dav`)
+ - the files and storage layer, including path handling (`lib/private/Files`)
+ - server-side encryption (`apps/encryption`)
+ - the security middleware (CSRF, CSP, brute-force protection, rate limits) under `lib/private/AppFramework` and `lib/private/Security`
+ - outgoing requests: `IClientService` and the local address checks in `IRemoteHostValidator`
+- Asymmetries: a restriction enforced on one path to data or an action but not on another.
+- Oracles: any difference in a response that reveals something the requester cannot see. User enumeration is an accepted risk.
+- Also in scope: the apps shipped under `apps/`.
+- Also in scope: the vendored `3rdparty/sabre/` (WebDAV, CalDAV, CardDAV, vObject, XML) and `3rdparty/nextcloud/` packages.
+- The rest of `3rdparty/` is in scope only where Nextcloud code reaches a defect in it in an exploitable way.
+- Out of scope: `build/`, `tests/`, the development tools in `vendor-bin/`, and the compiled frontend bundles in `dist/`.
+
+## How to exercise it
+- Run `apachectl start` first. Apache then serves `/src` at `http://localhost/` with `.htaccess` enabled.
+- The instance uses SQLite. Its data directory is `/var/lib/nextcloud/data`, outside the web root.
+- Accounts: `admin`/`admin`, and `alice`/`alice` and `bob`/`bob` as plain users, each in a group of their own.
+- Enabled: the apps from `apps/` that are enabled by default, plus admin_audit, encryption (enabled, but server-side encryption is not switched on), files_external and user_ldap.
+- The `testing` app is a CI helper, not enabled and out of scope.
+- Shipped apps that live in their own repositories (activity, notifications, text, circles, …) are not installed.
+- Try `curl -u alice:alice -X PROPFIND http://localhost/remote.php/dav/files/alice/`.
+- `occ` runs `/src/occ` as `www-data`, the user Apache runs as, and `as-www-data` runs any other command as that user. Use `occ` to create more users, groups and shares, and to enable other apps from `apps/` a scenario needs.
+- PHPUnit: `as-www-data composer run test -- tests/lib/` or `-- apps//tests`. Some tests are written for CI and can leave the instance unusable. Run only the tests for the code you are looking at, and run `nextcloud-reset` if the instance stops working.
+- PHP errors are not shown in responses. Nextcloud logs to `/var/lib/nextcloud/data/nextcloud.log`.
+- Not available here: Redis, an LDAP server, and the S3, Swift and Azure object stores. Tests that need them are skipped.
+
+## How you rate severity
+Score every finding with CVSS 3.1 and report the vector.
+
+Examples from published advisories:
+- GHSA-99gw-ww6p-f2rr, view-only shares on external storage or team folders are writeable: `CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:L` (7.1, high).
+- GHSA-x43f-gmgh-vvjj, SQL injection in a column type parameter: `CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:N` (8.2, high).
+- GHSA-jgcj-v42r-9922, two-factor authentication bypass via pending session token replay: `CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:L/I:H/A:N` (5.9, medium).
+- GHSA-vq3v-jv6f-6xp2, mail contact autocomplete bypasses the admin's user enumeration restrictions: `CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N` (4.3, medium).
+- GHSA-p3qw-7gwx-wg24, file drop links for end-to-end encrypted folders could drop files into other folders of the share owner: `CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:N/I:L/A:N` (3.5, low).
+- GHSA-8mpv-ggq8-hf3w, view-only guests could see deleted Collectives pages in the trash bin: `CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:U/C:L/I:N/A:N` (2.6, low).
+
+## Anything to leave alone
+- Self-XSS, missing hardening headers, clickjacking on pages without sensitive actions, and anything only exploitable with `debug` enabled.
+- Weak settings that are configurable by the admin (password policy, share expiry) are not vulnerabilities.
+- The `admin`/`admin` account and other choices made only for this image.
+- What admins can do by design: install and enable apps, configure external storages, run `occ`, execute code, and read data on the server.
+- Accepted risks from the official threat model:
+ - user enumeration
+ - version disclosure
+ - content spoofing
+ - brute force from rotating IP addresses
+ - one app reading another app's tables or config
+- SSRF: many features fetch from other hosts by design, so a blind request to a public host is not a finding. Full SSRF is in scope, including reaching local or private addresses.
+- Local external storage follows symlinks with web server privileges and is trusted.
+- Features that are disabled by default and carry an insecurity warning, such as the LibreOffice preview provider.
+
+## Reports and patches
+- One report per root cause, naming the commit, the affected file and function, the principal, the boundary crossed, and every affected route.
+- The build leaves `.htaccess` (the install appends its `ErrorDocument` block), `lib/composer/composer/installed.json`, `installed.php` and `InstalledVersions.php` (the development install) changed in `/src`. Leave them out of patches.
+- Include a reproducer: HTTP requests against the instance above, or a PHPUnit test that fails before the fix.
+- Patches should be minimal, fix the check where it is enforced rather than in one caller, and say whether sibling routes have the same gap. Match the surrounding code: tabs, and an SPDX header in new files.
diff --git a/.oss-scanner/threat_model.md.license b/.oss-scanner/threat_model.md.license
new file mode 100644
index 0000000000000..8c5ffd5249446
--- /dev/null
+++ b/.oss-scanner/threat_model.md.license
@@ -0,0 +1,2 @@
+SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
+SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/build/files-checker.php b/build/files-checker.php
index f2381e2e22280..2e1603b2a4b4a 100644
--- a/build/files-checker.php
+++ b/build/files-checker.php
@@ -24,6 +24,7 @@
'.mailmap',
'.nextcloudignore',
'.npmignore',
+ '.oss-scanner',
'.php-cs-fixer.dist.php',
'.pre-commit-config.yaml',
'.tag',