Files
leadchat/lib/linear/queries.rb
Shivam Mishra 67112647e8 fix: escape special characters in Linear GraphQL queries (#13490)
Creating a Linear issue from Chatwoot fails with a GraphQL parse error
when the title, description, or search term contains double quotes. For
example, a description like `the sender is "Bot"` produces this broken
query:

```graphql
issueCreate(input: { description: "the sender is "Bot"" })
```

Linear's API rejects this with `Syntax Error: Expected ":", found
String`. This affects issue creation, issue linking, and issue search —
any flow where user-provided text is interpolated into a GraphQL query.

The `graphql_value` helper was only escaping newlines (`\n`) but not
quotes, backslashes, or other characters that are meaningful inside a
GraphQL string literal. On top of that, `issue_link` and `search_issue`
bypassed `graphql_value` entirely, using raw string interpolation
instead.

The fix replaces the manual `gsub` escaping with Ruby's `to_json`, which
produces a properly escaped, double-quoted string that handles all
special characters. This is a minimal, well-understood substitution —
`to_json` on a Ruby string returns a valid JSON string literal, which is
also a valid GraphQL string literal since GraphQL uses the same escaping
rules. The `issue_link` mutation and `search_issue` query are updated to
route their parameters through `graphql_value` instead of raw
interpolation.

The `team_entities_query` and `linked_issues` methods in `queries.rb`
also use raw interpolation, but their inputs are system-generated IDs
and URLs rather than user-provided text, so they're left as-is to keep
this change focused.
2026-02-09 16:18:04 +05:30

105 lines
1.8 KiB
Ruby

module Linear::Queries
TEAMS_QUERY = <<~GRAPHQL.freeze
query {
teams {
nodes {
id
name
}
}
}
GRAPHQL
def self.team_entities_query(team_id)
<<~GRAPHQL
query {
users {
nodes {
id
name
}
}
projects {
nodes {
id
name
}
}
workflowStates(
filter: { team: { id: { eq: "#{team_id}" } } }
) {
nodes {
id
name
}
}
issueLabels(
filter: { team: { id: { eq: "#{team_id}" } } }
) {
nodes {
id
name
}
}
}
GRAPHQL
end
def self.search_issue(term)
<<~GRAPHQL
query {
searchIssues(term: #{Linear::Mutations.graphql_value(term)}) {
nodes {
id
title
description
identifier
state {
name
color
}
}
}
}
GRAPHQL
end
def self.linked_issues(url)
<<~GRAPHQL
query {
attachmentsForURL(url: "#{url}") {
nodes {
id
title
issue {
id
identifier
title
description
priority
createdAt
url
assignee {
name
avatarUrl
}
state {
name
color
}
labels {
nodes{
id
name
color
description
}
}
}
}
}
}
GRAPHQL
end
end