@formio/uag 1.12.1 → 1.12.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.fetchExternalData = void 0;
4
4
  const template_1 = require("../template");
5
+ const utils_1 = require("./utils");
5
6
  const core_1 = require("@formio/core");
6
7
  const SchemaBuilder_1 = require("./SchemaBuilder");
7
8
  const lodash_1 = require("lodash");
@@ -251,7 +252,7 @@ async function fetchResourceData(component, search_value, authInfo, project, con
251
252
  const query = {};
252
253
  // Add search filter if provided
253
254
  if (search_value && component.searchField) {
254
- query[`data.${component.searchField}__regex`] = `/${search_value}/i`;
255
+ query[`data.${component.searchField}__regex`] = `/${(0, utils_1.escapeSearchPattern)(search_value)}/i`;
255
256
  }
256
257
  // Add component filter if configured (interpolate the filter string)
257
258
  if (component.filter) {
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.findSubmission = void 0;
4
4
  const template_1 = require("../template");
5
+ const utils_1 = require("./utils");
5
6
  const lodash_1 = require("lodash");
6
7
  const SchemaBuilder_1 = require("./SchemaBuilder");
7
8
  const error = require('debug')('formio:uag:findSubmission:error');
@@ -34,19 +35,33 @@ const findSubmission = async (project) => {
34
35
  }, true);
35
36
  }
36
37
  if (criterion.operator === 'regex') {
38
+ // The value is a pattern by request here, so it is passed through
39
+ // unescaped. It is still compiled first: the server drops a filter
40
+ // it cannot compile, which would widen the search instead of
41
+ // failing it, so a bad pattern is better reported back.
42
+ try {
43
+ new RegExp(criterion.search_value);
44
+ }
45
+ catch (err) {
46
+ return project.mcpResponse(template_1.ResponseTemplate.submissionSearchError, {
47
+ form: form.form,
48
+ searchQuery: search_query,
49
+ error: `"${criterion.search_value}" is not a valid regular expression for "${criterion.data_path}". Use the "contains" operator to search for it literally.`
50
+ }, true);
51
+ }
37
52
  query[`data.${criterion.data_path}__regex`] = `/${criterion.search_value}/i`;
38
53
  }
39
54
  else if (criterion.operator === 'equals') {
40
55
  query[`data.${criterion.data_path}`] = criterion.search_value;
41
56
  }
42
57
  else if (criterion.operator === 'contains') {
43
- query[`data.${criterion.data_path}__regex`] = `/${criterion.search_value}/i`;
58
+ query[`data.${criterion.data_path}__regex`] = `/${(0, utils_1.escapeSearchPattern)(criterion.search_value)}/i`;
44
59
  }
45
60
  else if (criterion.operator === 'starts_with') {
46
- query[`data.${criterion.data_path}__regex`] = `/^${criterion.search_value}/i`;
61
+ query[`data.${criterion.data_path}__regex`] = `/^${(0, utils_1.escapeSearchPattern)(criterion.search_value)}/i`;
47
62
  }
48
63
  else if (criterion.operator === 'ends_with') {
49
- query[`data.${criterion.data_path}__regex`] = `/${criterion.search_value}$/i`;
64
+ query[`data.${criterion.data_path}__regex`] = `/${(0, utils_1.escapeSearchPattern)(criterion.search_value)}$/i`;
50
65
  }
51
66
  else if (criterion.operator === 'greater_than') {
52
67
  query[`data.${criterion.data_path}__gt`] = criterion.search_value;
@@ -1,4 +1,24 @@
1
1
  import { ZodRawShape } from "zod";
2
+ /**
3
+ * Escape a literal value so it can be carried in a `__regex` query parameter.
4
+ *
5
+ * Operators like "contains" promise a literal match, so every regular expression
6
+ * metacharacter in the value has to be neutered first. Left raw, a search for
7
+ * "joe.thompson@example.com" becomes a pattern whose dots match any character,
8
+ * and a value such as "[" is not a valid pattern at all — the server compiles
9
+ * these with a try/catch and silently drops the filter when compilation fails,
10
+ * which turns a narrow search into one that matches everything.
11
+ *
12
+ * Forward slashes get a further step. The server reads the parameter as
13
+ * `/pattern/flags` and takes the pattern with `[^/]+`, so an embedded slash
14
+ * truncates the pattern and the remainder is parsed as flags — again dropping
15
+ * the filter. Writing it as the equivalent `\x2f` escape keeps the character
16
+ * out of the transport while still matching a literal slash.
17
+ *
18
+ * @param value - The literal text to search for.
19
+ * @returns The value as a regular expression pattern that matches it literally.
20
+ */
21
+ export declare const escapeSearchPattern: (value: string) => string;
2
22
  export type ToolInfo = {
3
23
  name?: string;
4
24
  title?: string;
@@ -1,2 +1,25 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.escapeSearchPattern = void 0;
4
+ const lodash_1 = require("lodash");
5
+ /**
6
+ * Escape a literal value so it can be carried in a `__regex` query parameter.
7
+ *
8
+ * Operators like "contains" promise a literal match, so every regular expression
9
+ * metacharacter in the value has to be neutered first. Left raw, a search for
10
+ * "joe.thompson@example.com" becomes a pattern whose dots match any character,
11
+ * and a value such as "[" is not a valid pattern at all — the server compiles
12
+ * these with a try/catch and silently drops the filter when compilation fails,
13
+ * which turns a narrow search into one that matches everything.
14
+ *
15
+ * Forward slashes get a further step. The server reads the parameter as
16
+ * `/pattern/flags` and takes the pattern with `[^/]+`, so an embedded slash
17
+ * truncates the pattern and the remainder is parsed as flags — again dropping
18
+ * the filter. Writing it as the equivalent `\x2f` escape keeps the character
19
+ * out of the transport while still matching a literal slash.
20
+ *
21
+ * @param value - The literal text to search for.
22
+ * @returns The value as a regular expression pattern that matches it literally.
23
+ */
24
+ const escapeSearchPattern = (value) => (0, lodash_1.escapeRegExp)(value).replace(/\//g, '\\x2f');
25
+ exports.escapeSearchPattern = escapeSearchPattern;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@formio/uag",
3
- "version": "1.12.1",
3
+ "version": "1.12.2",
4
4
  "mcpName": "io.form/uag",
5
5
  "description": "The Form.io Universal Agent Gateway (UAG).",
6
6
  "main": "lib/index.js",
@@ -30,7 +30,7 @@
30
30
  "author": "Form.io Developers",
31
31
  "license": "MIT",
32
32
  "dependencies": {
33
- "@formio/appserver": "^2.13.1",
33
+ "@formio/appserver": "^2.13.2",
34
34
  "@formio/core": "2.8.0",
35
35
  "@modelcontextprotocol/sdk": "1.30.0",
36
36
  "cors": "^2.8.6",