@@ -8,13 +8,6 @@ const logger = createLogger('JiraConnector')
88
99const PAGE_SIZE = 50
1010
11- /**
12- * Escapes a value for use inside JQL double-quoted strings.
13- */
14- function escapeJql ( value : string ) : string {
15- return value . replace ( / \\ / g, '\\\\' ) . replace ( / " / g, '\\"' )
16- }
17-
1811/**
1912 * Computes a SHA-256 hash of the given content.
2013 */
@@ -147,9 +140,9 @@ export const jiraConnector: ConnectorConfig = {
147140
148141 const cloudId = await getJiraCloudId ( domain , accessToken )
149142
150- let jql = `project = " ${ escapeJql ( projectKey ) } " ORDER BY updated DESC`
143+ let jql = `project = ${ projectKey } ORDER BY updated DESC`
151144 if ( jqlFilter . trim ( ) ) {
152- jql = `project = " ${ escapeJql ( projectKey ) } " AND (${ jqlFilter . trim ( ) } ) ORDER BY updated DESC`
145+ jql = `project = ${ projectKey } AND (${ jqlFilter . trim ( ) } ) ORDER BY updated DESC`
153146 }
154147
155148 const startAt = cursor ? Number ( cursor ) : 0
@@ -251,19 +244,13 @@ export const jiraConnector: ConnectorConfig = {
251244 return { valid : false , error : 'Max issues must be a positive number' }
252245 }
253246
254- const jql = sourceConfig . jql as string | undefined
255- if ( jql ?. trim ( ) ) {
256- if ( / \b ( d e l e t e | d r o p | t r u n c a t e | i n s e r t | u p d a t e | a l t e r | c r e a t e | g r a n t | r e v o k e ) \b / i. test ( jql ) ) {
257- return { valid : false , error : 'Invalid JQL filter' }
258- }
259- }
247+ const jqlFilter = ( sourceConfig . jql as string | undefined ) ?. trim ( ) || ''
260248
261249 try {
262250 const cloudId = await getJiraCloudId ( domain , accessToken )
263251
264- // Verify the project exists by running a minimal search
265252 const params = new URLSearchParams ( )
266- params . append ( 'jql' , `project = " ${ escapeJql ( projectKey ) } " ` )
253+ params . append ( 'jql' , `project = ${ projectKey } ` )
267254 params . append ( 'maxResults' , '0' )
268255
269256 const url = `https://api.atlassian.com/ex/jira/${ cloudId } /rest/api/3/search?${ params . toString ( ) } `
@@ -287,6 +274,29 @@ export const jiraConnector: ConnectorConfig = {
287274 return { valid : false , error : `Failed to validate: ${ response . status } - ${ errorText } ` }
288275 }
289276
277+ if ( jqlFilter ) {
278+ const filterParams = new URLSearchParams ( )
279+ filterParams . append ( 'jql' , `project = ${ projectKey } AND (${ jqlFilter } )` )
280+ filterParams . append ( 'maxResults' , '0' )
281+
282+ const filterUrl = `https://api.atlassian.com/ex/jira/${ cloudId } /rest/api/3/search?${ filterParams . toString ( ) } `
283+ const filterResponse = await fetchWithRetry (
284+ filterUrl ,
285+ {
286+ method : 'GET' ,
287+ headers : {
288+ Accept : 'application/json' ,
289+ Authorization : `Bearer ${ accessToken } ` ,
290+ } ,
291+ } ,
292+ VALIDATE_RETRY_OPTIONS
293+ )
294+
295+ if ( ! filterResponse . ok ) {
296+ return { valid : false , error : 'Invalid JQL filter. Check syntax and field names.' }
297+ }
298+ }
299+
290300 return { valid : true }
291301 } catch ( error ) {
292302 const message = error instanceof Error ? error . message : 'Failed to validate configuration'
0 commit comments