11// Logic to get and store the current list of public Fastly IPs from the Fastly API: https://www.fastly.com/documentation/reference/api/utils/public-ip-list/
22
3- import ipaddr , { IPv4 , IPv6 } from 'ipaddr.js'
4-
5- type IPRangeArr = [ IPv4 | IPv6 , number ] [ ]
6-
73// Default returned from ➜ curl "https://api.fastly.com/public-ip-list"
8- export const DEFAULT_FASTLY_IPS : IPRangeArr = [
4+ export const DEFAULT_FASTLY_IPS : string [ ] = [
95 '23.235.32.0/20' ,
106 '43.249.72.0/22' ,
117 '103.244.50.0/24' ,
@@ -25,21 +21,22 @@ export const DEFAULT_FASTLY_IPS: IPRangeArr = [
2521 '185.31.16.0/22' ,
2622 '199.27.72.0/21' ,
2723 '199.232.0.0/16' ,
28- ] . map ( ( cidr ) => ipaddr . parseCIDR ( cidr ) )
24+ ]
2925
30- let ipRangeCache : IPRangeArr = [ ]
26+ let ipCache : string [ ] = [ ]
3127
32- export async function getPublicFastlyIPs ( ) : Promise < IPRangeArr > {
28+ export async function getPublicFastlyIPs ( ) : Promise < string [ ] > {
3329 // Don't fetch the list in dev & testing, just use the defaults
3430 if ( process . env . NODE_ENV !== 'production' ) {
35- ipRangeCache = DEFAULT_FASTLY_IPS
31+ ipCache = DEFAULT_FASTLY_IPS
3632 }
3733
38- if ( ipRangeCache . length ) {
39- return ipRangeCache
34+ if ( ipCache . length ) {
35+ return ipCache
4036 }
4137
4238 const endpoint = 'https://api.fastly.com/public-ip-list'
39+ let ips : string [ ] = [ ]
4340 let attempt = 0
4441
4542 while ( attempt < 3 ) {
@@ -50,8 +47,8 @@ export async function getPublicFastlyIPs(): Promise<IPRangeArr> {
5047 }
5148 const data = await response . json ( )
5249 if ( data && Array . isArray ( data . addresses ) ) {
53- ipRangeCache = data . addresses . map ( ( cidr : string ) => ipaddr . parseCIDR ( cidr ) )
54- return ipRangeCache
50+ ips = data . addresses
51+ break
5552 } else {
5653 throw new Error ( 'Invalid response structure' )
5754 }
@@ -60,22 +57,25 @@ export async function getPublicFastlyIPs(): Promise<IPRangeArr> {
6057 `Failed to fetch Fastly IPs: ${ error . message } . Retrying ${ 3 - attempt } more times` ,
6158 )
6259 attempt ++
60+ if ( attempt >= 3 ) {
61+ ips = DEFAULT_FASTLY_IPS
62+ }
6363 }
6464 }
6565
66- ipRangeCache = DEFAULT_FASTLY_IPS
67- return ipRangeCache
66+ ipCache = ips
67+ return ips
6868}
6969
7070// The IPs we check in the rate-limiter are in the form `X.X.X.X`
7171// But the IPs returned from the Fastly API are in the form `X.X.X.X/Y`
7272// For an IP in the rate-limiter, we want `X.X.X.*` to match `X.X.X.X/Y`
7373export async function isFastlyIP ( ip : string ) : Promise < boolean > {
7474 // If IPs aren't initialized, fetch them
75- if ( ! ipRangeCache . length ) {
75+ if ( ! ipCache . length ) {
7676 await getPublicFastlyIPs ( )
7777 }
78- if ( ! ip ) return false // localhost
79- const addr = ipaddr . parse ( ip )
80- return ipRangeCache . some ( ( range ) => addr . match ( range ) )
78+ const parts = ip . split ( '.' )
79+ const prefix = parts . slice ( 0 , 3 ) . join ( '.' )
80+ return ipCache . some ( ( fastlyIP ) => fastlyIP . startsWith ( prefix ) )
8181}
0 commit comments