Fix SQL injection, query value handling, and other security issues

- Quote all identifiers and validate operators, order strategies, join
  types, raw comparisons, and limit/offset/page in the query generator
- Treat 0 as a real query value; stop null from leaving unbound
  placeholders
- Return the WHERE clause and its values separately from sqlGenerator so
  delete/update no longer regex-parse generated SQL
- update(): target matched row ids, re-fetch reliably, and return
  success: false when no rows match
- Fix duplicated aliases, [object Object] select fields, AVERAGE -> AVG,
  escape GROUP_CONCAT separators
- Fix OFFSET without LIMIT and OFFSET on count queries
- Store boolean false as 0 on insert; call function values once
- Verify TLS certificates by default (new ssl_reject_unauthorized option)
- Confirm before importing an executable schema from an archive (--yes)
- Skip symlinks when reading extracted zip archives
- Only trim files matching the backup/export naming pattern
- Tighten default HTML sanitizer (no style/id attributes, no button)
- Stop typedef generation from mutating parent table schemas
- Pass config through DbSQL; handle BigInt values in results

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-14 02:10:35 +01:00
co-authored by Claude Opus 5.5
parent 155263a79f
commit b81b09324a
23 changed files with 603 additions and 382 deletions
+13 -7
View File
@@ -6,6 +6,7 @@ import type {
import sqlEqualityParser from "./sql-equality-parser";
type Params = {
/** Already-quoted column reference */
fieldName: string;
value?: SQLInsertGenValueType;
equality?: (typeof ServerQueryEqualities)[number];
@@ -15,6 +16,7 @@ type Params = {
},
string
>;
/** `value` is an already-quoted column reference, not a parameter */
isValueFieldValue?: boolean;
};
@@ -42,9 +44,13 @@ export default function sqlGenOperatorGen({
return { str: `${fieldName} IS NOT NULL` };
}
if (value) {
// `0` is a real value; only null / undefined / "" count as "no value"
const hasValue = value !== undefined && value !== null && value !== "";
if (hasValue) {
const finalValue = isValueFieldValue ? value : "?";
const finalParams = isValueFieldValue ? undefined : value;
const likeParams = isValueFieldValue ? undefined : `%${value}%`;
if (equality == "MATCH") {
return {
@@ -59,7 +65,7 @@ export default function sqlGenOperatorGen({
} else if (equality == "LIKE_LOWER") {
return {
str: `LOWER(${fieldName}) LIKE LOWER(${finalValue})`,
param: `%${finalParams}%`,
param: likeParams,
};
} else if (equality == "LIKE_LOWER_RAW") {
return {
@@ -69,7 +75,7 @@ export default function sqlGenOperatorGen({
} else if (equality == "LIKE") {
return {
str: `${fieldName} LIKE ${finalValue}`,
param: `%${finalParams}%`,
param: likeParams,
};
} else if (equality == "LIKE_RAW") {
return {
@@ -79,7 +85,7 @@ export default function sqlGenOperatorGen({
} else if (equality == "NOT_LIKE_LOWER") {
return {
str: `LOWER(${fieldName}) NOT LIKE LOWER(${finalValue})`,
param: `%${finalParams}%`,
param: likeParams,
};
} else if (equality == "NOT_LIKE_LOWER_RAW") {
return {
@@ -89,7 +95,7 @@ export default function sqlGenOperatorGen({
} else if (equality == "NOT LIKE") {
return {
str: `${fieldName} NOT LIKE ${finalValue}`,
param: finalParams,
param: likeParams,
};
} else if (equality == "NOT LIKE_RAW") {
return {
@@ -137,12 +143,12 @@ export default function sqlGenOperatorGen({
} else if (equality) {
return {
str: `${fieldName} ${sqlEqualityParser(equality)} ?`,
param: value,
param: value ?? "",
};
} else {
return {
str: `${fieldName} = ?`,
param: value,
param: value ?? "",
};
}
}