11/** @vitest -environment node */
2+ import { sha256Hex } from '@sim/security/hash'
23import { NextRequest } from 'next/server'
34import { beforeEach , describe , expect , it , vi } from 'vitest'
45import { OrchestrationError } from '@/lib/core/orchestration/types'
@@ -11,6 +12,7 @@ const mocks = vi.hoisted(() => ({
1112 consumeAttempt : vi . fn ( ) ,
1213 logError : vi . fn ( ) ,
1314 completeSetupOAuth : vi . fn ( ) ,
15+ authenticateSession : vi . fn ( ) ,
1416} ) )
1517
1618vi . mock ( '@sim/logger' , ( ) => ( {
@@ -21,7 +23,7 @@ vi.mock('@/lib/knowledge/application/github-setup', () => ({
2123} ) )
2224vi . mock ( '@/lib/api/server/routes' , ( ) => ( {
2325 internalSessionAuth : {
24- authenticate : async ( ) => ( { kind : 'session' , userId : 'admin' , sessionId : 'browser' } ) ,
26+ authenticate : mocks . authenticateSession ,
2527 } ,
2628} ) )
2729vi . mock ( '@/lib/core/utils/urls' , ( ) => ( { getBaseUrl : ( ) => 'https://sim.test' } ) )
@@ -127,6 +129,55 @@ describe('GitHub managed OAuth failure presentation', () => {
127129 provider : 'github-repositories' ,
128130 failure : 'failed' ,
129131 errorClass : 'unexpected' ,
132+ stage : 'enrollment_completion' ,
133+ errorType : 'Error' ,
134+ fingerprint : sha256Hex ( 'member@example.com ghu_token' ) . slice ( 0 , 12 ) ,
135+ } )
136+ } )
137+
138+ it ( 'identifies a wrapped database failure without logging SQL, parameters, or provider data' , async ( ) => {
139+ const cause = Object . assign ( new Error ( 'duplicate key for member@example.com' ) , {
140+ name : 'PostgresError' ,
141+ code : '23505' ,
142+ detail : 'ghu_private_token' ,
143+ } )
144+ mocks . consumeAttempt . mockResolvedValue ( attempt )
145+ mocks . completeOAuth . mockRejectedValueOnce (
146+ new Error ( 'Failed query: INSERT INTO credential\nparams: ghu_private_token' , { cause } )
147+ )
148+ const response = await completeCallback ( )
149+ expect ( response . headers . get ( 'location' ) ) . toContain ( 'oauth=failed' )
150+ expect ( mocks . logError ) . toHaveBeenCalledExactlyOnceWith ( 'Managed OAuth authorization failed' , {
151+ provider : 'github-repositories' ,
152+ failure : 'failed' ,
153+ errorClass : 'unexpected' ,
154+ stage : 'enrollment_completion' ,
155+ errorType : 'PostgresError' ,
156+ databaseCode : '23505' ,
157+ fingerprint : sha256Hex ( cause . message ) . slice ( 0 , 12 ) ,
158+ } )
159+ const logged = JSON . stringify ( mocks . logError . mock . calls )
160+ expect ( logged ) . not . toContain ( 'member@example.com' )
161+ expect ( logged ) . not . toContain ( 'ghu_private_token' )
162+ expect ( logged ) . not . toContain ( 'INSERT' )
163+ } )
164+
165+ it ( 'does not log arbitrary error names or codes as diagnostic metadata' , async ( ) => {
166+ mocks . consumeAttempt . mockResolvedValue ( attempt )
167+ mocks . completeOAuth . mockRejectedValueOnce (
168+ Object . assign ( new Error ( 'private provider response' ) , {
169+ name : 'ghu_private_token' ,
170+ code : 'client_secret=private' ,
171+ } )
172+ )
173+ await completeCallback ( )
174+ expect ( mocks . logError ) . toHaveBeenCalledExactlyOnceWith ( 'Managed OAuth authorization failed' , {
175+ provider : 'github-repositories' ,
176+ failure : 'failed' ,
177+ errorClass : 'unexpected' ,
178+ stage : 'enrollment_completion' ,
179+ errorType : 'UnknownError' ,
180+ fingerprint : sha256Hex ( 'private provider response' ) . slice ( 0 , 12 ) ,
130181 } )
131182 } )
132183
@@ -149,7 +200,40 @@ describe('GitHub managed OAuth failure presentation', () => {
149200describe ( 'GitHub installation setup OAuth return target' , ( ) => {
150201 beforeEach ( ( ) => {
151202 vi . clearAllMocks ( )
203+ mocks . authenticateSession . mockResolvedValue ( {
204+ kind : 'session' ,
205+ userId : 'admin' ,
206+ sessionId : 'browser' ,
207+ } )
152208 } )
209+
210+ it . each ( [ 'session_authentication' , 'setup_completion' ] ) (
211+ 'identifies an unexpected failure during %s without exposing its message' ,
212+ async ( stage ) => {
213+ mocks . consumeAttempt . mockResolvedValue ( {
214+ ...attempt ,
215+ returnTo : 'github-installation' ,
216+ organizationId : 'organization' ,
217+ completionId,
218+ } )
219+ const error = new TypeError ( 'private callback data' )
220+ if ( stage === 'session_authentication' ) {
221+ mocks . authenticateSession . mockRejectedValueOnce ( error )
222+ } else {
223+ mocks . completeSetupOAuth . mockRejectedValueOnce ( error )
224+ }
225+ const response = await completeCallback ( )
226+ expect ( response . headers . get ( 'location' ) ) . toContain ( 'oauth=failed' )
227+ expect ( mocks . logError ) . toHaveBeenCalledExactlyOnceWith ( 'Managed OAuth authorization failed' , {
228+ provider : 'github-repositories' ,
229+ failure : 'failed' ,
230+ errorClass : 'unexpected' ,
231+ stage,
232+ errorType : 'TypeError' ,
233+ fingerprint : sha256Hex ( error . message ) . slice ( 0 , 12 ) ,
234+ } )
235+ }
236+ )
153237 it ( 'resumes only the server-owned setup after the guarded OAuth completion' , async ( ) => {
154238 mocks . consumeAttempt . mockResolvedValue ( {
155239 ...attempt ,
0 commit comments