rez_core 4.0.59 → 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.59",
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,54 +35,80 @@ 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.
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 it's an array expand placeholders (?, ?, ?)
49
- const placeholders = val.map(() => '?').join(', ');
50
- parsedQuery = parsedQuery.replace(new RegExp(`:${key}`, 'g'), `(${placeholders})`);
51
- values.push(...val); // flatten values
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
+ );
64
+ }
65
+ values.push(...val);
52
66
  } else {
53
- parsedQuery = parsedQuery.replace(new RegExp(`:${key}`, 'g'), '?');
67
+ parsedQuery = parsedQuery.replace(
68
+ new RegExp(`:\\b${key}\\b`, 'g'),
69
+ '?',
70
+ );
54
71
  values.push(val);
55
72
  }
56
73
  });
57
74
 
58
- return parsedQuery;
75
+ // ✅ Enclose each clause in parentheses for proper AND grouping
76
+ return `(${parsedQuery})`;
59
77
  });
60
78
 
61
79
  whereSQL = `WHERE ${clauseParts.join(' AND ')}`;
62
80
  }
63
81
 
82
+ // ✅ Wrap identifiers in backticks for MySQL or double quotes for Postgres
64
83
  const rawSQL = `
65
- SELECT ${column} AS tab_value, COUNT(*) AS tab_value_count
84
+ SELECT
85
+ ${column} AS tab_value,
86
+ COUNT(*) AS tab_value_count
66
87
  FROM ${tableName}
67
88
  ${whereSQL}
68
89
  GROUP BY ${column}
90
+ ORDER BY tab_value_count DESC
69
91
  `;
70
92
 
71
93
  const rows = await this.dataSource.query(rawSQL, values);
72
94
 
95
+ // ✅ Handle total count safely
73
96
  const total = rows.reduce(
74
- (sum, r) => sum + parseInt(r.tab_value_count, 10),
97
+ (sum, r) => sum + Number(r.tab_value_count || 0),
75
98
  0,
76
99
  );
77
100
 
101
+ // ✅ Ensure consistent response
78
102
  return [
79
103
  { tab_value: 'All', tab_value_count: total },
80
104
  ...rows.map((r) => ({
81
105
  tab_value: r.tab_value ?? 'UNKNOWN',
82
- tab_value_count: parseInt(r.tab_value_count, 10),
106
+ tab_value_count: Number(r.tab_value_count || 0),
83
107
  })),
84
108
  ];
85
109
  }
86
110
 
111
+
87
112
 
88
113
  async applyFilterWrapper(dto: FilterRequestDto) {
89
114
  const {