rez_core 4.0.60 → 4.0.61

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rez_core",
3
- "version": "4.0.60",
3
+ "version": "4.0.61",
4
4
  "description": "",
5
5
  "author": "",
6
6
  "private": false,
@@ -28,7 +28,6 @@ export class FilterService {
28
28
  private readonly skipAppCodeFilterEntities = ['ORGP'];
29
29
  private readonly skipOrgFilterEntities = ['ORGP'];
30
30
 
31
-
32
31
  private async gettab_value_counts(
33
32
  tableName: string,
34
33
  column: string | undefined,
@@ -36,27 +35,33 @@ export class FilterService {
36
35
  ) {
37
36
  if (!column) return [];
38
37
 
38
+ // ✅ Validate tableName and column to prevent SQL injection
39
+ if (!/^[a-zA-Z0-9_]+$/.test(tableName) || !/^[a-zA-Z0-9_]+$/.test(column)) {
40
+ throw new Error('Invalid table or column name');
41
+ }
42
+
39
43
  let whereSQL = '';
40
44
  const values: any[] = [];
41
45
 
42
46
  if (whereClauses.length > 0) {
43
47
  const clauseParts = whereClauses.map((clause) => {
44
- let parsedQuery = clause.query.replace(/\be\./g, ''); // remove e. prefix
48
+ // Safely remove alias "e." only at word boundaries
49
+ let parsedQuery = clause.query.replace(/\be\./g, '');
45
50
 
46
51
  Object.entries(clause.params).forEach(([key, val]) => {
47
52
  if (Array.isArray(val)) {
48
- // if the query uses '=' but the param is array → convert to IN
49
- if (parsedQuery.includes(`= :${key}`)) {
50
- parsedQuery = parsedQuery.replace(`= :${key}`, `IN (:${key})`);
53
+ // Handle both '=' and 'IN' dynamically
54
+ if (parsedQuery.match(new RegExp(`=\\s*:${key}\\b`))) {
55
+ parsedQuery = parsedQuery.replace(
56
+ new RegExp(`=\\s*:${key}\\b`, 'g'),
57
+ `IN (${val.map(() => '?').join(', ')})`,
58
+ );
59
+ } else {
60
+ parsedQuery = parsedQuery.replace(
61
+ new RegExp(`:\\b${key}\\b`, 'g'),
62
+ val.map(() => '?').join(', '),
63
+ );
51
64
  }
52
-
53
- // generate (?, ?, ?) placeholders
54
- const placeholders = val.map(() => '?').join(', ');
55
- parsedQuery = parsedQuery.replace(
56
- new RegExp(`:\\b${key}\\b`, 'g'),
57
- placeholders,
58
- );
59
-
60
65
  values.push(...val);
61
66
  } else {
62
67
  parsedQuery = parsedQuery.replace(
@@ -67,31 +72,38 @@ export class FilterService {
67
72
  }
68
73
  });
69
74
 
70
- return parsedQuery;
75
+ // ✅ Enclose each clause in parentheses for proper AND grouping
76
+ return `(${parsedQuery})`;
71
77
  });
72
78
 
73
79
  whereSQL = `WHERE ${clauseParts.join(' AND ')}`;
74
80
  }
75
81
 
82
+ // ✅ Wrap identifiers in backticks for MySQL or double quotes for Postgres
76
83
  const rawSQL = `
77
- SELECT ${column} AS tab_value, COUNT(*) AS tab_value_count
84
+ SELECT
85
+ ${column} AS tab_value,
86
+ COUNT(*) AS tab_value_count
78
87
  FROM ${tableName}
79
88
  ${whereSQL}
80
89
  GROUP BY ${column}
90
+ ORDER BY tab_value_count DESC
81
91
  `;
82
92
 
83
93
  const rows = await this.dataSource.query(rawSQL, values);
84
94
 
95
+ // ✅ Handle total count safely
85
96
  const total = rows.reduce(
86
- (sum, r) => sum + parseInt(r.tab_value_count, 10),
97
+ (sum, r) => sum + Number(r.tab_value_count || 0),
87
98
  0,
88
99
  );
89
100
 
101
+ // ✅ Ensure consistent response
90
102
  return [
91
103
  { tab_value: 'All', tab_value_count: total },
92
104
  ...rows.map((r) => ({
93
105
  tab_value: r.tab_value ?? 'UNKNOWN',
94
- tab_value_count: parseInt(r.tab_value_count, 10),
106
+ tab_value_count: Number(r.tab_value_count || 0),
95
107
  })),
96
108
  ];
97
109
  }