Security scanning looks for sensitive files and pages your app serves to anyone on the internet: repository metadata, environment and credential files, debug pages, directory listings and forgotten backups. Automated scanners request exactly these paths within minutes of a new site appearing. Security scanning requests them first and tells you if one is readable.
It is off by default for every app, including static sites. A clean result does not mean your app is secure: it only means none of the paths below was exposed when the scan ran.
Turning it on#
Open the app's Monitoring tab and switch on Scan for exposed files in the Security scan section. You need permission to manage apps (owners and admins have it). Turning scanning on or off is recorded in your organization's audit log.
The first scan starts within a few minutes. After that, the app is scanned:
- after each new production release goes live,
- at least once every 24 hours, and
- when you choose Scan now. After a scan starts, Scan now is available again five minutes later.
Turning scanning off stops further scans and closes any open findings without notifying anyone.
What is checked#
On each public production address of the app (its nimbusapps.ca address and, once verified, its custom domain), a scan requests these paths:
| Path | Kind | Reported when the response |
|---|---|---|
/.git/HEAD | Repository metadata | starts with ref: refs/ or is a commit hash |
/.git/config | Repository metadata | contains a [core] section and is not HTML |
/.env | Credentials | has at least two NAME=value lines and is not HTML |
/.env.production | Credentials | has at least two NAME=value lines and is not HTML |
/.npmrc | Credentials | contains _authToken= or registry= and is not HTML |
/.aws/credentials | Credentials | has a profile section with aws_access_key_id |
/server-status | Diagnostic page | is Apache's server status page |
/phpinfo.php | Diagnostic page | is a phpinfo() page |
/actuator/env | Diagnostic page | is Spring Boot's environment endpoint |
/ | Directory listing | is an Index of / listing |
/.DS_Store | Backup or data file | is a macOS folder metadata file |
/backup.sql | Backup or data file | contains CREATE TABLE or INSERT INTO and is not HTML |
/dump.sql | Backup or data file | contains CREATE TABLE or INSERT INTO and is not HTML |
/db.sqlite3 | Backup or data file | is an SQLite database file |
/wp-config.php.bak | Backup or data file | is a WordPress configuration containing DB_PASSWORD |
A path is reported only when the response has a 2xx status and its content looks like the real file or page. Redirects, error pages, and apps that answer every path with their home page are never reported. Before checking, the scan requests one random path that cannot exist, and discards any match identical to that response.
Apps that require sign-in are not scanned, and are shown as protected by sign-in rather than as clean. Apps that are stopped or have no live production release are not scanned either.
How the requests look to your app#
Scan requests reach your app the same way a visitor's do, one at a time and a quarter of a second apart: about 16 requests per address per scan. They carry this user agent:
NimbusLand-SecurityScan/1 (+https://nimbusland.ca/docs/security-scanning)
Scan requests are left out of your app's Traffic and Analytics, and never count toward the Visitors seeing errors or Site slow alerts, so an app that answers unknown paths with server errors cannot raise an alert because of a scan.
Responses are compared in memory, at most 64 KiB of each, and discarded. Nothing from a response is stored or logged. A finding records only which check matched, on which URL, and when.
Findings#
Each open finding names the URL, what was exposed, why it matters and how to fix it.
- Owners and admins get an email when a scan opens findings: one email per scan, listing all of them.
- Notification destinations in your organization settings can subscribe to the
security.findingevent. A webhook receives one signed delivery per finding, with the app, the URL, the check, when it opened and a link to the console. Response content is never included. - The app's Overview shows a Needs attention item while any finding is open.
- A finding resolves on its own when a later scan that reached every path no longer matches it. If a scan could not reach the app, findings stay open and the section says so. Resolved findings stay listed for 30 days.
Each finding notifies once. Resolving a finding notifies nobody.
Fixing what is found#
- Repository metadata (
.git): serve a build or public directory rather than the repository root, or stop your web server from serving.git. Rotate anything that was ever committed. - Environment and credential files: remove the file from the served directory and set the values as environment variables or secrets instead. Rotate every credential the file held, and assume it has been read.
- Diagnostic pages: delete
phpinfo.php, turn off Apache'smod_statusor restrict it to local requests, and stop exposing Spring Boot'senvendpoint. - Directory listing: turn it off (
autoindex offin nginx,Options -Indexesin Apache) or add an index page. - Backups and data files: move them out of the web root, and treat the data in them as exposed.
Limits#
- Only the paths above are requested. A scan does not crawl, fuzz, sign in, test TLS or security headers, or look at non-production environments.
- Only the custom domain itself is scanned, not its
wwwvariant. The two normally serve the same app, so an exposure on one is usually on both. - A scan of one app takes at most about two minutes. An address still being scanned after that is reported as "scan took too long", and its open findings stay open until a scan completes.
- A clean scan reads "no exposed files found among N common checks". It is not a statement that the app is secure.
The always-on page check is a separate check that runs for every app.
Also available as Markdown.