Skip to content

Adjust Tenable integration by requiring auth and validating host URLs - #8510

Open
DMedina6 wants to merge 17 commits into
masterfrom
tenable-url-security
Open

Adjust Tenable integration by requiring auth and validating host URLs#8510
DMedina6 wants to merge 17 commits into
masterfrom
tenable-url-security

Conversation

@DMedina6

@DMedina6 DMedina6 commented Aug 10, 2026

Copy link
Copy Markdown
Contributor
  • Add JwtAuthGuard to TenableController (login + proxy endpoints)

  • Add TENABLE_HOST_URLS config option (comma-separated allowlist)

  • Reject login requests whose host_url origin isn't in the allowlist

  • Restricts host_url to http/https, trims whitespace before validation, rejects (instead of silently stripping) any extra path/query/fragment

@DMedina6
DMedina6 requested review from Amndeep7 and a lite review from Copilot August 10, 2026 17:53
@DMedina6 DMedina6 self-assigned this Aug 10, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR tightens the backend Tenable integration by requiring JWT authentication on Tenable endpoints and introducing a configurable allowlist to validate client-supplied Tenable host URLs before performing login/proxy operations.

Changes:

  • Added JwtAuthGuard to Tenable controller endpoints.
  • Introduced TENABLE_HOST_URLS (comma-separated) allowlist and host-origin validation for Tenable login requests.
  • Exposed getTenableHostUrls() through config layers and documented the new env var in .env-example.

Reviewed changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
apps/backend/src/tenable/tenable.module.ts Wires configuration access into the Tenable feature module.
apps/backend/src/tenable/tenable.controller.ts Adds JWT auth guard and validates host_url against an allowlist before login.
apps/backend/src/config/config.service.ts Exposes a getTenableHostUrls() accessor for controllers/services.
apps/backend/config/app_config.ts Adds parsing for the TENABLE_HOST_URLS env var.
apps/backend/.env-example Documents the new TENABLE_HOST_URLS configuration.
Suppressed comments (1)

apps/backend/src/tenable/tenable.controller.ts:99

  • The allowlist check validates only the origin, but the code continues to use the raw host_url (which may include a path/query/fragment) to build fullUrl and to store creds.host_url in the session. This can cause proxying to an unintended base path and diverges from what was actually allowlisted. Normalize to the parsed origin for both the login request and the stored session host.
      const fullUrl = `${host_url.replace(/\/$/, '')}/rest/currentUser`;
      const result = await axios.get(fullUrl, {
        headers: {
          'x-apikey': `accesskey=${accesskey}; secretkey=${secretkey}`
        }

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread apps/backend/src/tenable/tenable.module.ts
Comment thread apps/backend/config/app_config.ts Outdated
Comment thread apps/backend/.env-example Outdated
Comment thread apps/backend/.env-example Outdated
Comment thread apps/backend/config/app_config.ts Outdated
Comment thread apps/backend/src/tenable/tenable.controller.ts Outdated
Comment thread apps/backend/src/tenable/tenable.controller.ts Outdated
Comment thread apps/backend/src/tenable/tenable.controller.ts Outdated
Comment thread apps/backend/src/tenable/tenable.controller.ts
Comment thread problem-context.md

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

delete this

Comment thread apps/backend/.env-example Outdated
Comment thread apps/backend/.env-example

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we'll also need to update the envvar wiki page

Comment thread apps/backend/.env-example

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we'll also probably need to update the heimdall helm chart

Comment thread apps/backend/config/app_config.ts Outdated
Comment thread apps/backend/src/tenable/tenable.controller.ts Outdated
Comment thread apps/backend/src/tenable/tenable.controller.ts Outdated
return null;
}

const match = allowlist.includes(parsed.origin);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nominally this will fail if they are using a nonstandard port for the protocol which is allowed, i.e. we add 443 as a port by default for https but they could use basically whatever else and it would be valid but would crash here cause "For URLs using the ftp:, http:, https:, ws:, and wss: schemes, the protocol followed by //, followed by the host. Same as host, the port is only included if it's not the default for the protocol." - https://developer.mozilla.org/en-US/docs/Web/API/URL/origin

i'm pretty sure the envvar says to not include a port on it

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ports / non-default ports can now be specified for both the environment variable / app-config, and the frontend / client side input so it's consistent and flexible

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread apps/backend/src/tenable/tenable.controller.ts Outdated
Comment thread apps/frontend/src/utilities/tenable_util.ts Outdated
Comment thread apps/backend/.env-example

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we'll also probably need to update the tenable integration part of the integrations wiki page

// Assign the Tenable credentials to the session
req.session.tenable = {host_url, accesskey, secretkey};
// Store the normalized, allowlisted origin rather than the raw client value.
req.session.tenable = {host_url: allowedHostUrl, accesskey, secretkey};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI review comment that seems like it could be feasible so please review it:

JwtAuthGuard proves only that the caller has a Heimdall account; these credentials remain associated solely with the browser’s Express session. The Tenable “sign out” UI only clears client state, and /users/logout revokes the JWT without clearing session.tenable. On a shared/reused browser session, a subsequently authenticated user can call the proxy with the prior user’s Tenable keys. Store the authenticated Heimdall user ID with the credentials, verify it on every proxy request, and clear the credentials on both Tenable and Heimdall logout. Please add an A-login → logout → B-login → proxy-is-denied regression test.

You don't need to write an explicit test unless you want to, but please do validate by hand that logging in with a second account does not allow one to reuse the access/secret key.

Comment thread apps/backend/config/app_config.ts Outdated
}
// Only protocol + hostname + port are allowed; ports are permitted since
// Tenable.SC may run on a non-default port.
const hasExtra =

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

both of the hasExtra processing areas need to exclude everything aside from hostname + protocol + optionally port

considering that this processing code is the same, maybe we can extract out to some common utility function and use it in both places.

@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants