@@ -13,6 +13,52 @@ interface TestIntegrationBody {
1313 credentials : Record < string , string >
1414}
1515
16+ function isPrivateOrLocalHost ( hostname : string ) : boolean {
17+ const host = hostname . toLowerCase ( )
18+
19+ if ( host === 'localhost' || host === '::1' || host === '[::1]' ) return true
20+ if ( host . endsWith ( '.localhost' ) || host . endsWith ( '.local' ) ) return true
21+
22+ // IPv4 checks
23+ const ipv4Match = host . match ( / ^ ( \d { 1 , 3 } ) \. ( \d { 1 , 3 } ) \. ( \d { 1 , 3 } ) \. ( \d { 1 , 3 } ) $ / )
24+ if ( ipv4Match ) {
25+ const octets = ipv4Match . slice ( 1 ) . map ( ( n ) => Number ( n ) )
26+ if ( octets . some ( ( o ) => Number . isNaN ( o ) || o < 0 || o > 255 ) ) return true
27+
28+ const [ a , b ] = octets
29+ if ( a === 10 ) return true
30+ if ( a === 127 ) return true
31+ if ( a === 169 && b === 254 ) return true
32+ if ( a === 172 && b >= 16 && b <= 31 ) return true
33+ if ( a === 192 && b === 168 ) return true
34+ if ( a === 0 ) return true
35+ }
36+
37+ // Common IPv6 local/private forms
38+ const normalized = host . replace ( / ^ \[ | \] $ / g, '' )
39+ if ( normalized . startsWith ( 'fc' ) || normalized . startsWith ( 'fd' ) ) return true // ULA
40+ if ( normalized . startsWith ( 'fe80:' ) ) return true // link-local
41+
42+ return false
43+ }
44+
45+ function getSafeKenerOrigin ( baseUrl ?: string ) : string | null {
46+ const candidate = ( baseUrl && baseUrl . trim ( ) ) || 'https://emberlystat.us'
47+
48+ let parsed : URL
49+ try {
50+ parsed = new URL ( candidate )
51+ } catch {
52+ return null
53+ }
54+
55+ if ( parsed . protocol !== 'http:' && parsed . protocol !== 'https:' ) return null
56+ if ( parsed . username || parsed . password ) return null
57+ if ( isPrivateOrLocalHost ( parsed . hostname ) ) return null
58+
59+ return parsed . origin
60+ }
61+
1662interface TestResult {
1763 ok : boolean
1864 message : string
@@ -122,9 +168,13 @@ async function testGitHub(pat: string, org?: string): Promise<TestResult> {
122168}
123169
124170async function testKener ( apiKey : string , baseUrl ?: string ) : Promise < TestResult > {
125- const url = ( baseUrl || 'https://emberlystat.us' ) . replace ( / \/ $ / , '' )
171+ const origin = getSafeKenerOrigin ( baseUrl )
172+ if ( ! origin ) {
173+ return { ok : false , message : 'Invalid Kener base URL' }
174+ }
175+
126176 try {
127- const res = await fetch ( `${ url } /api/v4/monitors` , {
177+ const res = await fetch ( `${ origin } /api/v4/monitors` , {
128178 headers : apiKey ? { Authorization : `Bearer ${ apiKey } ` } : { } ,
129179 signal : AbortSignal . timeout ( 8000 ) ,
130180 } )
0 commit comments