Skip to content

Commit 25247eb

Browse files
Merge pull request #511 from Opteo/escaping-query-constraints
Escaping query constraints
2 parents 8daa3c7 + 8940c99 commit 25247eb

1 file changed

Lines changed: 29 additions & 6 deletions

File tree

src/query.ts

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,33 @@ export function buildFromClause(entity: ReportOptions["entity"]): FromClause {
100100
return ` ${QueryKeywords.FROM} ${entity}` as const;
101101
}
102102

103+
function formatGaqlString(val: string): string {
104+
const len = val.length;
105+
const startsWithSingle = val.startsWith("'");
106+
const endsWithSingle = val.endsWith("'");
107+
const startsWithDouble = val.startsWith('"');
108+
const endsWithDouble = val.endsWith('"');
109+
110+
// Returns the original string if it seems correctly quoted already.
111+
if (len >= 2) {
112+
if (startsWithSingle && endsWithSingle && !val.slice(1, -1).includes("'")) {
113+
return val;
114+
}
115+
if (startsWithDouble && endsWithDouble && !val.slice(1, -1).includes('"')) {
116+
return val;
117+
}
118+
}
119+
120+
// Always use double quotes and escape internal double quotes and backslashes
121+
if (val.includes('"') || val.includes("\\")) {
122+
const escapedVal = val.replace(/\\/g, "\\\\").replace(/"/g, '\\"'); // Escape backslashes first, then double quotes
123+
return `"${escapedVal}"`;
124+
} else {
125+
// No escaping needed, just wrap in double quotes
126+
return `"${val}"`;
127+
}
128+
}
129+
103130
export function validateConstraintKeyAndValue(
104131
key: ConstraintKey,
105132
op: ConstraintOperation,
@@ -116,18 +143,14 @@ export function validateConstraintKeyAndValue(
116143
if (dateConstants.includes(val as DateConstant)) {
117144
return { op, val };
118145
}
119-
120-
return {
121-
op: "=",
122-
val: new RegExp(/^'.*'$|^".*"$/g).test(val) ? val : `"${val}"`,
123-
}; // must start and end in either single or double quotation marks
146+
return { op: "=", val: formatGaqlString(val) };
124147
}
125148

126149
if (Array.isArray(val)) {
127150
const stringifiedValue = val
128151
.map((v: number | string) => {
129152
if (typeof v === "string") {
130-
return `"${v}"`;
153+
return formatGaqlString(v);
131154
} else {
132155
return convertNumericEnumToString(key, v);
133156
}

0 commit comments

Comments
 (0)