@carbonorm/carbonnode 3.5.5 → 3.5.7
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/dist/api/orm/builders/ConditionBuilder.d.ts +3 -2
- package/dist/index.cjs.js +49 -29
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +49 -29
- package/dist/index.esm.js.map +1 -1
- package/package.json +5 -2
- package/src/api/executors/SqlExecutor.ts +1 -2
- package/src/api/orm/builders/ConditionBuilder.ts +48 -22
- package/src/api/orm/builders/JoinBuilder.ts +1 -3
- package/src/api/orm/queries/DeleteQueryBuilder.ts +1 -1
- package/src/api/orm/queries/PostQueryBuilder.ts +0 -1
- package/src/api/orm/queries/SelectQueryBuilder.ts +1 -1
- package/src/api/orm/queries/UpdateQueryBuilder.ts +1 -1
|
@@ -8,7 +8,37 @@ export abstract class ConditionBuilder<
|
|
|
8
8
|
G extends OrmGenerics
|
|
9
9
|
> extends AggregateBuilder<G> {
|
|
10
10
|
|
|
11
|
-
protected
|
|
11
|
+
protected aliasMap: Record<string, string> = {};
|
|
12
|
+
|
|
13
|
+
protected initAlias(baseTable: string, joins?: any): void {
|
|
14
|
+
this.aliasMap = { [baseTable]: baseTable };
|
|
15
|
+
|
|
16
|
+
if (!joins) return;
|
|
17
|
+
|
|
18
|
+
for (const joinType in joins) {
|
|
19
|
+
for (const raw in joins[joinType]) {
|
|
20
|
+
const [table, alias] = raw.split(' ');
|
|
21
|
+
this.aliasMap[alias || table] = table;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
protected isColumnRef(ref: string): boolean {
|
|
27
|
+
if (typeof ref !== 'string' || !ref.includes('.')) return false;
|
|
28
|
+
|
|
29
|
+
const [prefix, column] = ref.split('.', 2);
|
|
30
|
+
const tableName = this.aliasMap[prefix] || prefix;
|
|
31
|
+
const table = this.config.C6?.TABLES?.[tableName];
|
|
32
|
+
if (!table) return false;
|
|
33
|
+
|
|
34
|
+
const fullKey = `${tableName}.${column}`;
|
|
35
|
+
if (table.COLUMNS && (fullKey in table.COLUMNS)) return true;
|
|
36
|
+
if (table.COLUMNS && Object.values(table.COLUMNS).includes(ref)) return true;
|
|
37
|
+
|
|
38
|
+
this.config.verbose && console.log(`[COLUMN REF] ${ref} is not a valid column reference`);
|
|
39
|
+
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
12
42
|
|
|
13
43
|
abstract build(table: string): SqlBuilderResult;
|
|
14
44
|
|
|
@@ -27,17 +57,6 @@ export abstract class ConditionBuilder<
|
|
|
27
57
|
C6C.ST_DISTANCE_SPHERE
|
|
28
58
|
]);
|
|
29
59
|
|
|
30
|
-
private isTableReference(val: any): boolean {
|
|
31
|
-
if (typeof val !== 'string' || !val.includes('.')) return false;
|
|
32
|
-
const [prefix, column] = val.split('.');
|
|
33
|
-
const tableName = this.aliasMappings[prefix] ?? prefix;
|
|
34
|
-
return (
|
|
35
|
-
typeof this.config.C6?.TABLES[tableName] === 'object' &&
|
|
36
|
-
val in this.config.C6.TABLES[tableName].COLUMNS &&
|
|
37
|
-
this.config.C6.TABLES[tableName].COLUMNS[val] === column
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
60
|
private validateOperator(op: string) {
|
|
42
61
|
if (!this.OPERATORS.has(op)) {
|
|
43
62
|
throw new Error(`Invalid or unsupported SQL operator detected: '${op}'`);
|
|
@@ -83,8 +102,14 @@ export abstract class ConditionBuilder<
|
|
|
83
102
|
}
|
|
84
103
|
}
|
|
85
104
|
|
|
105
|
+
const leftIsCol = this.isColumnRef(column);
|
|
106
|
+
const rightIsCol = typeof value === 'string' && this.isColumnRef(value);
|
|
107
|
+
|
|
108
|
+
if (!leftIsCol && !rightIsCol) {
|
|
109
|
+
throw new Error(`Potential SQL injection detected: '${column} ${op} ${value}'`);
|
|
110
|
+
}
|
|
111
|
+
|
|
86
112
|
this.validateOperator(op);
|
|
87
|
-
const leftIsRef: boolean = this.isTableReference(column);
|
|
88
113
|
|
|
89
114
|
if (op === C6C.MATCH_AGAINST && Array.isArray(value)) {
|
|
90
115
|
const [search, mode] = value;
|
|
@@ -109,7 +134,7 @@ export abstract class ConditionBuilder<
|
|
|
109
134
|
break;
|
|
110
135
|
}
|
|
111
136
|
|
|
112
|
-
if (!
|
|
137
|
+
if (!leftIsCol) {
|
|
113
138
|
throw new Error(`MATCH_AGAINST requires a table reference as the left operand. Column '${column}' is not a valid table reference.`);
|
|
114
139
|
}
|
|
115
140
|
const matchClause = `(MATCH(${column}) ${againstClause})`;
|
|
@@ -118,9 +143,11 @@ export abstract class ConditionBuilder<
|
|
|
118
143
|
}
|
|
119
144
|
|
|
120
145
|
if ((op === C6C.IN || op === C6C.NOT_IN) && Array.isArray(value)) {
|
|
121
|
-
const placeholders = value.map(v =>
|
|
146
|
+
const placeholders = value.map(v =>
|
|
147
|
+
this.isColumnRef(v) ? v : this.addParam(params, column, v)
|
|
148
|
+
).join(', ');
|
|
122
149
|
const normalized = op.replace('_', ' ');
|
|
123
|
-
if (!
|
|
150
|
+
if (!leftIsCol) {
|
|
124
151
|
throw new Error(`IN operator requires a table reference as the left operand. Column '${column}' is not a valid table reference.`);
|
|
125
152
|
}
|
|
126
153
|
return `( ${column} ${normalized} (${placeholders}) )`;
|
|
@@ -128,26 +155,25 @@ export abstract class ConditionBuilder<
|
|
|
128
155
|
|
|
129
156
|
if (op === C6C.BETWEEN || op === 'NOT BETWEEN') {
|
|
130
157
|
if (!Array.isArray(value) || value.length !== 2) {
|
|
131
|
-
throw new Error(`BETWEEN operator requires an array of two values`);
|
|
158
|
+
throw new Error(`BETWEEN operator requires an array of two values. Received: ${JSON.stringify(value)}`);
|
|
132
159
|
}
|
|
133
160
|
const [start, end] = value;
|
|
134
|
-
if (!
|
|
161
|
+
if (!leftIsCol) {
|
|
135
162
|
throw new Error(`BETWEEN operator requires a table reference as the left operand. Column '${column}' is not a valid table reference.`);
|
|
136
163
|
}
|
|
137
164
|
return `(${column}) ${op.replace('_', ' ')} ${this.addParam(params, column, start)} AND ${this.addParam(params, column, end)}`;
|
|
138
165
|
}
|
|
139
166
|
|
|
140
|
-
const rightIsRef: boolean = this.isTableReference(value);
|
|
141
167
|
|
|
142
|
-
if (
|
|
168
|
+
if (leftIsCol && rightIsCol) {
|
|
143
169
|
return `(${column}) ${op} ${value}`;
|
|
144
170
|
}
|
|
145
171
|
|
|
146
|
-
if (
|
|
172
|
+
if (leftIsCol && !rightIsCol) {
|
|
147
173
|
return `(${column}) ${op} ${this.addParam(params, column, value)}`;
|
|
148
174
|
}
|
|
149
175
|
|
|
150
|
-
if (
|
|
176
|
+
if (rightIsCol) {
|
|
151
177
|
return `(${this.addParam(params, column, column)}) ${op} ${value}`;
|
|
152
178
|
}
|
|
153
179
|
|
|
@@ -11,9 +11,7 @@ export abstract class JoinBuilder<G extends OrmGenerics> extends ConditionBuilde
|
|
|
11
11
|
|
|
12
12
|
for (const raw in joinArgs[joinType]) {
|
|
13
13
|
const [table, alias] = raw.split(' ');
|
|
14
|
-
|
|
15
|
-
this.aliasMappings[alias] = table;
|
|
16
|
-
}
|
|
14
|
+
this.aliasMap[alias || table] = table;
|
|
17
15
|
const onClause = this.buildBooleanJoinedConditions(joinArgs[joinType][raw], true, params);
|
|
18
16
|
const joinSql = alias ? `\`${table}\` AS \`${alias}\`` : `\`${table}\``;
|
|
19
17
|
sql += ` ${joinKind} JOIN ${joinSql} ON ${onClause}`;
|
|
@@ -6,8 +6,8 @@ export class DeleteQueryBuilder<G extends OrmGenerics> extends JoinBuilder<G> {
|
|
|
6
6
|
build(
|
|
7
7
|
table: string
|
|
8
8
|
): SqlBuilderResult {
|
|
9
|
-
this.aliasMappings = {};
|
|
10
9
|
const params = this.useNamedParams ? {} : [];
|
|
10
|
+
this.initAlias(table, this.request.JOIN);
|
|
11
11
|
|
|
12
12
|
let sql = `DELETE \`${table}\` FROM \`${table}\``;
|
|
13
13
|
|
|
@@ -14,7 +14,6 @@ export class PostQueryBuilder<G extends OrmGenerics> extends ConditionBuilder<G>
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
build(table: string) {
|
|
17
|
-
this.aliasMappings = {};
|
|
18
17
|
const verb = C6C.REPLACE in this.request ? C6C.REPLACE : C6C.INSERT;
|
|
19
18
|
const body = verb in this.request ? this.request[verb] : this.request;
|
|
20
19
|
const keys = Object.keys(body);
|
|
@@ -8,8 +8,8 @@ export class SelectQueryBuilder<G extends OrmGenerics> extends PaginationBuilder
|
|
|
8
8
|
table: string,
|
|
9
9
|
isSubSelect: boolean = false
|
|
10
10
|
): SqlBuilderResult {
|
|
11
|
-
this.aliasMappings = {};
|
|
12
11
|
const args = this.request;
|
|
12
|
+
this.initAlias(table, args.JOIN);
|
|
13
13
|
const params = this.useNamedParams ? {} : [];
|
|
14
14
|
const selectList = args.SELECT ?? ['*'];
|
|
15
15
|
const selectFields = selectList
|
|
@@ -8,9 +8,9 @@ export class UpdateQueryBuilder<G extends OrmGenerics> extends PaginationBuilder
|
|
|
8
8
|
build(
|
|
9
9
|
table: string,
|
|
10
10
|
): SqlBuilderResult {
|
|
11
|
-
this.aliasMappings = {};
|
|
12
11
|
const args = this.request;
|
|
13
12
|
const params = this.useNamedParams ? {} : [];
|
|
13
|
+
this.initAlias(table, args.JOIN);
|
|
14
14
|
let sql = `UPDATE \`${table}\``;
|
|
15
15
|
|
|
16
16
|
if (args.JOIN) {
|