Skip to main content

Connected Apps

Vulkro checks every *.connectedApp-meta.xml (SFDX) and *.connectedApp (MDAPI) file. A Connected App defines how an outside system gets OAuth access to your org.

vulkro-sf scan ./force-app # metadata in source
vulkro-sf org connected-apps --target-org my-prod # the live org

Full OAuth scope (High)​

<oauthConfig>
<scopes>Full</scopes>
<scopes>RefreshToken</scopes>
</oauthConfig>

Full gives the token everything the authorizing user can do. Combined with a refresh token, it is the setup behind the 2025 OAuth token-theft breaches. Fix: request only the scopes the integration needs (Api, RefreshToken, OpenId), and add IP restrictions and a short token lifetime in Setup.

Cleartext callback URL (High)​

<callbackUrl>http://localhost:8080/oauth/callback</callbackUrl>

The authorization code travels unencrypted and can be intercepted and exchanged for a token (CWE-319). Fix: use https://.

Hardcoded consumer secret (Critical) and key (Medium)​

<consumerKey>3MVG9YDQS5WtC11oOXz7BaqUgw8</consumerKey>
<consumerSecret>1234567890ABCDEFGHIJ</consumerSecret>

A consumer secret in deployable metadata is a leaked OAuth client secret: anyone with repository or deploy-artifact access holds it. The key is the OAuth client_id, less sensitive but still per org. Fix: remove both elements, configure the app in the target org's Setup, and rotate the leaked secret now.

Deploy-time placeholders are not flagged: {{var}}, ${var}, <% var %>, REPLACE_ME, TODO, TBD, placeholder, your_secret_here, and all-x or all-0 values.

Not covered in source​

  • A service-to-service app using Password auth where a JWT bearer flow with a certificate fits better.
  • IP restrictions and login policies set in Setup, not in metadata. The live-org audits (org connected-apps, org oauth-risk) read these.

See also: Named Credentials, org connected-apps