Skip to content

Commit 4d1b154

Browse files
Robustness: top-level error handling, API timeout, token validation
- Wrap all three handlers in top-level try/catch that fails open - Add 2000ms Promise.race timeout on API calls in viewer request - Validate token format before API call (must contain a digit) Co-Authored-By: Claude (claude-opus-4-6) <noreply@anthropic.com>
1 parent 988b635 commit 4d1b154

8 files changed

Lines changed: 47 additions & 20 deletions

dist/originOverride.zip

57 Bytes
Binary file not shown.

dist/originResponse.zip

128 Bytes
Binary file not shown.

dist/viewerRequest.zip

502 Bytes
Binary file not shown.

dist/viewerResponse.zip

67 Bytes
Binary file not shown.

handlerOriginResponse.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ const http_helpers = require("./helpers/http");
66
require("source-map-support").install();
77

88
module.exports.originResponse = async (event) => {
9+
let response = event.Records[0].cf.response;
10+
try {
911
let APIDomain;
1012
let crowdhandlerResponseID;
1113
let crowdhandlerToken;
@@ -14,7 +16,6 @@ module.exports.originResponse = async (event) => {
1416

1517
// Infer useful information from the request event and save it
1618
let request = event.Records[0].cf.request;
17-
let response = event.Records[0].cf.response;
1819
const requestHeaders = request.headers;
1920
const uri = request.uri;
2021

@@ -108,4 +109,8 @@ module.exports.originResponse = async (event) => {
108109
}
109110
}
110111
return response;
112+
} catch (error) {
113+
console.error('[CH] Unhandled error in originResponse - failing open:', error);
114+
return response;
115+
}
111116
};

handlerViewerRequest.js

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ const http_helpers = require("./helpers/http");
66
require("source-map-support").install();
77

88
module.exports.viewerRequest = async (event) => {
9+
let request = event.Records[0].cf.request;
10+
try {
911
// Environment Setup
1012
const APIDomain = "CROWDHANDLER_API_DOMAIN";
1113
// If failtrust is false, users that fail to check-in with CrowdHandler will be sent to waiting room.
@@ -18,7 +20,6 @@ module.exports.viewerRequest = async (event) => {
1820
const whitelabel = true;
1921

2022
// Extract request Meta Information
21-
let request = event.Records[0].cf.request;
2223
let requestHeaders = request.headers;
2324
const host = requestHeaders.host[0].value;
2425

@@ -139,6 +140,13 @@ module.exports.viewerRequest = async (event) => {
139140
tokenSource = 'new';
140141
}
141142

143+
// Validate token format - must contain at least one digit
144+
const validToken = /(.*\d+.*)/;
145+
if (token && !validToken.test(token)) {
146+
token = null;
147+
tokenSource = 'new';
148+
}
149+
142150
if (freshlyPromoted) {
143151
let redirectLocation
144152
if (queryString) {
@@ -157,9 +165,17 @@ module.exports.viewerRequest = async (event) => {
157165
};
158166
let response;
159167

160-
if (token) {
161-
try {
162-
response = await http_helpers.httpGET({
168+
let timeoutHandle;
169+
const timeoutPromise = new Promise((_, reject) => {
170+
timeoutHandle = setTimeout(() => {
171+
reject(new Error('API Communication Timed Out!'));
172+
}, http_helpers.API_TIMEOUT_MS);
173+
});
174+
175+
try {
176+
let apiCall;
177+
if (token) {
178+
apiCall = http_helpers.httpGET({
163179
headers: headers,
164180
hostname: APIDomain,
165181
method: "GET",
@@ -168,15 +184,8 @@ module.exports.viewerRequest = async (event) => {
168184
)}&lang=${encodeURIComponent(language)}`,
169185
port: 443,
170186
});
171-
} catch (error) {
172-
console.error('CrowdHandler API GET failed:', error);
173-
response = error;
174-
} finally {
175-
return response;
176-
}
177-
} else {
178-
try {
179-
response = await http_helpers.httpPOST(
187+
} else {
188+
apiCall = http_helpers.httpPOST(
180189
{
181190
headers: headers,
182191
hostname: APIDomain,
@@ -191,12 +200,14 @@ module.exports.viewerRequest = async (event) => {
191200
lang: language,
192201
})
193202
);
194-
} catch (error) {
195-
console.error('CrowdHandler API POST failed:', error);
196-
response = error;
197-
} finally {
198-
return response;
199203
}
204+
response = await Promise.race([apiCall, timeoutPromise]);
205+
} catch (error) {
206+
console.error('CrowdHandler API call failed:', error.message || error);
207+
response = error;
208+
} finally {
209+
clearTimeout(timeoutHandle);
210+
return response;
200211
}
201212
}
202213

@@ -299,4 +310,8 @@ module.exports.viewerRequest = async (event) => {
299310
}
300311

301312
return request;
313+
} catch (error) {
314+
console.error('[CH] Unhandled error in viewerRequest - failing open:', error);
315+
return request;
316+
}
302317
};

handlerViewerResponse.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ const helpers = require("./helpers/misc");
55
require("source-map-support").install();
66

77
module.exports.viewerResponse = (event, context, callback) => {
8-
const request = event.Records[0].cf.request;
98
const response = event.Records[0].cf.response;
9+
try {
10+
const request = event.Records[0].cf.request;
1011
const requestHeaders = request.headers;
1112
const uri = request.uri;
1213

@@ -53,4 +54,8 @@ module.exports.viewerResponse = (event, context, callback) => {
5354
});
5455

5556
return callback(null, response);
57+
} catch (error) {
58+
console.error('[CH] Unhandled error in viewerResponse - failing open:', error);
59+
return callback(null, response);
60+
}
5661
};

helpers/http.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
const https = require("https");
22

3+
export const API_TIMEOUT_MS = 2000;
4+
35
// Server error response (5xx, network errors) - respects failTrust setting
46
const dummyResponseData = {
57
result: {

0 commit comments

Comments
 (0)