mirror of
https://github.com/outline/outline.git
synced 2026-08-03 13:27:25 +03:00
@@ -1420,5 +1420,23 @@ describe("PostgresSearchProvider", () => {
|
||||
`"plugins"`
|
||||
);
|
||||
});
|
||||
it("should not escape colons outside of a phrase", () => {
|
||||
// pg-tsquery drops an unquoted colon but keeps a preceding backslash,
|
||||
// so escaping here would leave a dangling "\" operand.
|
||||
expect(
|
||||
PostgresSearchProvider.webSearchQuery("hoffe, gehts gut :) bei")
|
||||
).toBe("(hoffe|gehts&gut)&bei:*");
|
||||
expect(PostgresSearchProvider.webSearchQuery("sub:title:here")).toBe(
|
||||
"sub&title&here:*"
|
||||
);
|
||||
});
|
||||
it("should escape colons inside a phrase", () => {
|
||||
expect(PostgresSearchProvider.webSearchQuery(`"sub:title:here"`)).toBe(
|
||||
`"sub\\:title\\:here"`
|
||||
);
|
||||
expect(PostgresSearchProvider.webSearchQuery(`'sub:title'`)).toBe(
|
||||
`"sub\\:title":*`
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -840,9 +840,7 @@ export default class PostgresSearchProvider extends BaseSearchProvider {
|
||||
*/
|
||||
public static webSearchQuery(query: string): string {
|
||||
// limit length of search queries as we're using regex against untrusted input
|
||||
let limitedQuery = PostgresSearchProvider.escapeQuery(
|
||||
query.slice(0, PostgresSearchProvider.maxQueryLength)
|
||||
);
|
||||
let limitedQuery = query.slice(0, PostgresSearchProvider.maxQueryLength);
|
||||
|
||||
const quotedSearch =
|
||||
limitedQuery.startsWith('"') && limitedQuery.endsWith('"');
|
||||
@@ -867,6 +865,9 @@ export default class PostgresSearchProvider extends BaseSearchProvider {
|
||||
}
|
||||
}
|
||||
|
||||
// Escape only once the phrase delimiters above have settled.
|
||||
limitedQuery = PostgresSearchProvider.escapeTsQuery(limitedQuery);
|
||||
|
||||
return (
|
||||
queryParser()(
|
||||
// Although queryParser trims the query, looks like there's a
|
||||
@@ -882,14 +883,26 @@ export default class PostgresSearchProvider extends BaseSearchProvider {
|
||||
}
|
||||
|
||||
private static escapeQuery(query: string): string {
|
||||
return (
|
||||
query
|
||||
// replace "\" with escaped "\\" because sequelize.escape doesn't do it
|
||||
// see: https://github.com/sequelize/sequelize/issues/2950
|
||||
.replace(/\\/g, "\\\\")
|
||||
// replace ":" with escaped "\:" because it's a reserved character in tsquery
|
||||
// see: https://github.com/outline/outline/issues/6542
|
||||
.replace(/:/g, "\\:")
|
||||
// replace "\" with escaped "\\" because sequelize.escape doesn't do it
|
||||
// see: https://github.com/sequelize/sequelize/issues/2950
|
||||
return query.replace(/\\/g, "\\\\");
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes the characters that are reserved in tsquery, segment by segment so
|
||||
* that quoted phrases can be treated differently to the rest of the query.
|
||||
*
|
||||
* ":" only needs escaping inside a phrase, where pg-tsquery passes it through
|
||||
* verbatim (see: https://github.com/outline/outline/issues/6542). Elsewhere
|
||||
* pg-tsquery drops the colon but keeps a preceding backslash, which would
|
||||
* leave to_tsquery with a dangling escape character.
|
||||
*/
|
||||
private static escapeTsQuery(query: string): string {
|
||||
return query.replace(/"[^"]*"|'[^']*'|[\s\S]/g, (segment) =>
|
||||
// a match longer than the single character fallback is a quoted phrase
|
||||
segment.length > 1
|
||||
? segment.replace(/[\\:]/g, "\\$&")
|
||||
: segment.replace(/\\/g, "\\\\")
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user