@@ -20,7 +20,9 @@ const PAGE_SIZE = 100
2020 * fetches (`/api/now/table/{table}/{sys_id}`) likewise treat the sys_id as a
2121 * URL path segment and must be constrained to safe characters.
2222 */
23- const SYS_ID_PATTERN = / ^ [ a - f 0 - 9 ] { 32 } $ /
23+ const SYS_ID_PATTERN = / ^ [ a - f 0 - 9 ] { 32 } $ / i
24+ const NUMERIC_ID_PATTERN = / ^ \d + $ /
25+ const KB_CATEGORY_PATTERN = / ^ [ \w \- . / ] + $ /
2426
2527interface ServiceNowRecord {
2628 sys_id : string
@@ -363,18 +365,22 @@ function buildKBQuery(sourceConfig: Record<string, unknown>): string {
363365
364366 const workflowState = sourceConfig . workflowState as string | undefined
365367 if ( workflowState && workflowState !== 'all' ) {
366- parts . push ( `workflow_state=${ workflowState } ` )
368+ if ( NUMERIC_ID_PATTERN . test ( workflowState ) ) {
369+ parts . push ( `workflow_state=${ workflowState } ` )
370+ } else {
371+ logger . warn ( 'Skipping workflowState filter: value is not a numeric ID' , { workflowState } )
372+ }
367373 }
368374
369375 const kbCategory = sourceConfig . kbCategory as string | undefined
370376 const trimmedCategory = kbCategory ?. trim ( )
371377 if ( trimmedCategory ) {
372- if ( trimmedCategory . includes ( '^' ) ) {
373- logger . warn ( 'Skipping kbCategory filter: value contains "^" separator' , {
378+ if ( KB_CATEGORY_PATTERN . test ( trimmedCategory ) ) {
379+ parts . push ( `kb_category.label=${ trimmedCategory } ` )
380+ } else {
381+ logger . warn ( 'Skipping kbCategory filter: value contains disallowed characters' , {
374382 kbCategory : trimmedCategory ,
375383 } )
376- } else {
377- parts . push ( `kb_category.label=${ trimmedCategory } ` )
378384 }
379385 }
380386
@@ -390,12 +396,22 @@ function buildIncidentQuery(sourceConfig: Record<string, unknown>): string {
390396
391397 const incidentState = sourceConfig . incidentState as string | undefined
392398 if ( incidentState && incidentState !== 'all' ) {
393- parts . push ( `state=${ incidentState } ` )
399+ if ( NUMERIC_ID_PATTERN . test ( incidentState ) ) {
400+ parts . push ( `state=${ incidentState } ` )
401+ } else {
402+ logger . warn ( 'Skipping incidentState filter: value is not a numeric ID' , { incidentState } )
403+ }
394404 }
395405
396406 const incidentPriority = sourceConfig . incidentPriority as string | undefined
397407 if ( incidentPriority && incidentPriority !== 'all' ) {
398- parts . push ( `priority=${ incidentPriority } ` )
408+ if ( NUMERIC_ID_PATTERN . test ( incidentPriority ) ) {
409+ parts . push ( `priority=${ incidentPriority } ` )
410+ } else {
411+ logger . warn ( 'Skipping incidentPriority filter: value is not a numeric ID' , {
412+ incidentPriority,
413+ } )
414+ }
399415 }
400416
401417 parts . push ( 'ORDERBYDESCsys_updated_on' )
0 commit comments