@@ -16,6 +16,7 @@ import {
1616 verboseLog ,
1717 fetchJson ,
1818 isLikelyClerkTokenProblem ,
19+ validateApiBase ,
1920} from "./vibe-utils.js" ;
2021
2122// =============================================================================
@@ -31,6 +32,10 @@ const [vibeAuthErrors] = errorCauses({
3132 code : "AUTH_EXPIRED" ,
3233 message : "Token expired and refresh failed" ,
3334 } ,
35+ SecurityBlockError : {
36+ code : "SECURITY_BLOCK" ,
37+ message : "Security validation failed" ,
38+ } ,
3439 ConfigReadError : {
3540 code : "CONFIG_READ_ERROR" ,
3641 message : "Failed to read configuration file" ,
@@ -56,6 +61,7 @@ const {
5661 ConfigWriteError,
5762 TokenExchangeError,
5863 RefreshError,
64+ SecurityBlockError,
5965} = vibeAuthErrors ;
6066
6167// =============================================================================
@@ -715,6 +721,17 @@ const discoverOidc = async (issuer) => {
715721 * @returns {Promise<object> } Vibecodr token response
716722 */
717723const exchangeForVibecodrToken = async ( { apiBase, clerkAccessToken } ) => {
724+ // SECURITY: Validate apiBase before sending tokens to prevent credential exfiltration
725+ // A malicious or tampered apiBase could steal the Clerk access token
726+ const apiCheck = validateApiBase ( apiBase ) ;
727+ if ( ! apiCheck . valid ) {
728+ throw createError ( {
729+ ...SecurityBlockError ,
730+ message : `Refusing to send token to untrusted API: ${ apiCheck . reason } ` ,
731+ apiBase,
732+ } ) ;
733+ }
734+
718735 const url = `${ normalizeOrigin ( apiBase ) } /auth/cli/exchange` ;
719736 return fetchJson ( url , {
720737 method : "POST" ,
@@ -902,6 +919,12 @@ export const refreshVibecodrToken = async ({
902919 verbose,
903920 } ) ;
904921 } catch ( exchangeErr ) {
922+ // SECURITY: Security errors must propagate directly - never mask them
923+ // This ensures token theft attempts are clearly reported, not hidden as auth issues
924+ if ( exchangeErr ?. cause ?. code === "SECURITY_BLOCK" ) {
925+ throw exchangeErr ;
926+ }
927+
905928 const now = Math . floor ( Date . now ( ) / 1000 ) ;
906929
907930 if ( ! clerkRefreshToken ) {
0 commit comments