@@ -57,7 +57,7 @@ describe("exportOrbBatch() — reads review_audit, ships anonymized reversal-awa
5757 beforeEach ( ( ) => {
5858 resetMetrics ( ) ;
5959 process . env . ORB_ENABLED = "true" ;
60- process . env . ORB_WEBHOOK_SECRET = "test-secret" ;
60+ process . env . ORB_WEBHOOK_SECRET = "test-secret-at-least-32-bytes-long " ;
6161 process . env . ORB_APP_ID = "555" ;
6262 process . env . ORB_ANONYMIZE = "true" ;
6363 delete process . env . ORB_AIR_GAP ;
@@ -164,9 +164,32 @@ describe("exportOrbBatch() — reads review_audit, ships anonymized reversal-awa
164164 expect ( sig ) . toMatch ( / ^ s h a 2 5 6 = [ a - f 0 - 9 ] { 64 } $ / ) ;
165165 } ) ;
166166
167- it ( "falls back to GITHUB_APP_ID for the instance id and applies secret/anonymize defaults when ORB_* are unset" , async ( ) => {
167+ it ( "fails closed when anonymized export has no strong per-instance secret" , async ( ) => {
168+ delete process . env . ORB_WEBHOOK_SECRET ;
169+ delete process . env . ORB_ANONYMIZE ; // → defaults to "true"
170+ const db = makeDb ( ) ;
171+ await audit ( db , "owner/repo" , 1 , "gate_decision" , "merge" , "2026-01-01T00:00:00Z" ) ;
172+ await audit ( db , "owner/repo" , 1 , "pr_outcome" , "merged" , "2026-01-01T01:00:00Z" ) ;
173+ let called = false ;
174+ const n = await exportOrbBatch ( db , 200 , async ( ) => { called = true ; return new Response ( null , { status : 200 } ) ; } ) ;
175+ expect ( n ) . toBe ( 0 ) ;
176+ expect ( called ) . toBe ( false ) ;
177+ expect ( await renderMetrics ( ) ) . toContain ( `gittensory_orb_export_errors_total{reason="missing_anonymization_secret"} 1` ) ;
178+ } ) ;
179+
180+ it ( "fails closed when anonymized export has a weak per-instance secret" , async ( ) => {
181+ process . env . ORB_WEBHOOK_SECRET = "short-secret" ;
182+ const db = makeDb ( ) ;
183+ await audit ( db , "owner/repo" , 1 , "gate_decision" , "merge" , "2026-01-01T00:00:00Z" ) ;
184+ await audit ( db , "owner/repo" , 1 , "pr_outcome" , "merged" , "2026-01-01T01:00:00Z" ) ;
185+ let called = false ;
186+ const n = await exportOrbBatch ( db , 200 , async ( ) => { called = true ; return new Response ( null , { status : 200 } ) ; } ) ;
187+ expect ( n ) . toBe ( 0 ) ;
188+ expect ( called ) . toBe ( false ) ;
189+ } ) ;
190+
191+ it ( "falls back to GITHUB_APP_ID for the instance id while using a configured anonymization secret" , async ( ) => {
168192 delete process . env . ORB_APP_ID ; // → falls through to GITHUB_APP_ID
169- delete process . env . ORB_WEBHOOK_SECRET ; // → secret defaults to ""
170193 delete process . env . ORB_ANONYMIZE ; // → defaults to "true"
171194 ( process . env as NodeJS . Dict < string > ) . GITHUB_APP_ID = "999" ;
172195 const db = makeDb ( ) ;
0 commit comments