@@ -21,8 +21,9 @@ import {
21
21
import { configBlock } from './components' ;
22
22
import { getGitHubAppJWT } from './provider' ;
23
23
import { triggerExport , updateCommitWithPreviewLinks } from './sync' ;
24
- import type { GithubRuntimeContext } from './types' ;
25
- import { BRANCH_REF_PREFIX } from './utils' ;
24
+ import { handleIntegrationTask } from './tasks' ;
25
+ import type { GithubRuntimeContext , IntegrationTask } from './types' ;
26
+ import { arrayToHex , BRANCH_REF_PREFIX , safeCompare } from './utils' ;
26
27
import { handlePullRequestEvents , handlePushEvent , verifyGitHubWebhookSignature } from './webhooks' ;
27
28
28
29
const logger = Logger ( 'github' ) ;
@@ -38,6 +39,61 @@ const handleFetchEvent: FetchEventCallback<GithubRuntimeContext> = async (reques
38
39
) . pathname ,
39
40
} ) ;
40
41
42
+ async function verifyIntegrationSignature (
43
+ payload : string ,
44
+ signature : string ,
45
+ secret : string
46
+ ) : Promise < boolean > {
47
+ if ( ! signature ) {
48
+ return false ;
49
+ }
50
+
51
+ const algorithm = { name : 'HMAC' , hash : 'SHA-256' } ;
52
+ const enc = new TextEncoder ( ) ;
53
+ const key = await crypto . subtle . importKey ( 'raw' , enc . encode ( secret ) , algorithm , false , [
54
+ 'sign' ,
55
+ 'verify' ,
56
+ ] ) ;
57
+ const signed = await crypto . subtle . sign ( algorithm . name , key , enc . encode ( payload ) ) ;
58
+ const expectedSignature = arrayToHex ( signed ) ;
59
+
60
+ return safeCompare ( expectedSignature , signature ) ;
61
+ }
62
+
63
+ /**
64
+ * Handle integration tasks
65
+ */
66
+ router . post ( '/tasks' , async ( request ) => {
67
+ const signature = request . headers . get ( 'x-gitbook-integration-signature' ) ?? '' ;
68
+ const payloadString = await request . text ( ) ;
69
+
70
+ const verified = await verifyIntegrationSignature (
71
+ payloadString ,
72
+ signature ,
73
+ environment . signingSecret !
74
+ ) ;
75
+
76
+ if ( ! verified ) {
77
+ return new Response ( 'Invalid integration signature' , {
78
+ status : 400 ,
79
+ } ) ;
80
+ }
81
+
82
+ const { task } = JSON . parse ( payloadString ) as { task : IntegrationTask } ;
83
+ logger . debug ( 'verified & received integration task' , task ) ;
84
+
85
+ context . waitUntil (
86
+ ( async ( ) => {
87
+ await handleIntegrationTask ( context , task ) ;
88
+ } ) ( )
89
+ ) ;
90
+
91
+ return new Response ( JSON . stringify ( { acknowledged : true } ) , {
92
+ status : 200 ,
93
+ headers : { 'content-type' : 'application/json' } ,
94
+ } ) ;
95
+ } ) ;
96
+
41
97
/**
42
98
* Handle GitHub App webhook events
43
99
*/
0 commit comments