inibase 1.6.4 → 1.6.6

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/README.md CHANGED
@@ -634,6 +634,11 @@ const users = await db.get("user", undefined, {
634
634
  columns: ["!username", "!address.street"],
635
635
  });
636
636
 
637
+ // Columns accept dotted nested paths (e.g. "address.street" => nested field)
638
+ const nested = await db.get("user", undefined, {
639
+ columns: ["address.street", "hobbies.name"],
640
+ });
641
+
637
642
  // Get all columns explicitly with the "*" wildcard
638
643
  const all = await db.get("user", undefined, {
639
644
  columns: ["*"],
package/dist/cli.js CHANGED
@@ -180,7 +180,7 @@ rl.on("line", async (input) => {
180
180
  break;
181
181
  }
182
182
  if (!isSafeName(splitedInput[1]))
183
- console.log(`${textRed(" Err:")} Invalid table name, only alphanumeric characters, underscores and hyphens are allowed`);
183
+ console.log(`${textRed(" Err:")} Invalid table name`);
184
184
  else if (!(await isExists(join(path, splitedInput[1]))))
185
185
  console.log(`${textRed(" Err:")} Table doesn't exist`);
186
186
  else {
package/dist/index.d.ts CHANGED
@@ -79,6 +79,16 @@ export default class Inibase {
79
79
  constructor(database: string, mainFolder?: string, language?: ErrorLang);
80
80
  createError(name: ErrorCode, variable?: string | number | (string | number)[]): Error;
81
81
  private validateName;
82
+ /**
83
+ * Validates column paths used in the `columns` option.
84
+ *
85
+ * Each column may be a nested path of dot-separated names
86
+ * (e.g. "address.street", "hobbies.name") and may optionally
87
+ * start with "!" for exclusion. The "*" wildcard (select all)
88
+ * and its "!*" counterpart are special values and are skipped.
89
+ * Every segment of a path is validated individually.
90
+ */
91
+ private validateColumns;
82
92
  private validateSchema;
83
93
  private getFileExtension;
84
94
  private schemaToIdsPath;
package/dist/index.js CHANGED
@@ -64,6 +64,28 @@ export default class Inibase {
64
64
  validateName(name) {
65
65
  Utils.validateName(name, this.language);
66
66
  }
67
+ /**
68
+ * Validates column paths used in the `columns` option.
69
+ *
70
+ * Each column may be a nested path of dot-separated names
71
+ * (e.g. "address.street", "hobbies.name") and may optionally
72
+ * start with "!" for exclusion. The "*" wildcard (select all)
73
+ * and its "!*" counterpart are special values and are skipped.
74
+ * Every segment of a path is validated individually.
75
+ */
76
+ validateColumns(columns) {
77
+ for (const column of columns) {
78
+ if (column === "*" || column === "!*")
79
+ continue;
80
+ const path = column.startsWith("!") ? column.slice(1) : column;
81
+ for (const segment of path.split(".")) {
82
+ if (!segment)
83
+ this.validateName(column);
84
+ else
85
+ this.validateName(segment);
86
+ }
87
+ }
88
+ }
67
89
  validateSchema(schema) {
68
90
  Utils.validateSchema(schema, this.language);
69
91
  }
@@ -1109,9 +1131,7 @@ export default class Inibase {
1109
1131
  options.columns = Array.isArray(options.columns)
1110
1132
  ? options.columns
1111
1133
  : [options.columns];
1112
- for (const column of options.columns)
1113
- if (column !== "*")
1114
- this.validateName(column);
1134
+ this.validateColumns(options.columns);
1115
1135
  if (options.columns.length && !options.columns.includes("id"))
1116
1136
  options.columns.push("id");
1117
1137
  }
@@ -1356,11 +1376,9 @@ export default class Inibase {
1356
1376
  };
1357
1377
  this.validateName(tableName);
1358
1378
  if (options.columns)
1359
- for (const column of (Array.isArray(options.columns)
1379
+ this.validateColumns((Array.isArray(options.columns)
1360
1380
  ? options.columns
1361
- : [options.columns]))
1362
- if (column !== "*")
1363
- this.validateName(column);
1381
+ : [options.columns]));
1364
1382
  const tablePath = join(this.databasePath, tableName);
1365
1383
  await this.getTable(tableName);
1366
1384
  if (!globalConfig[this.databasePath].tables?.get(tableName)?.schema)
@@ -1443,11 +1461,9 @@ export default class Inibase {
1443
1461
  const renameList = [];
1444
1462
  this.validateName(tableName);
1445
1463
  if (options.columns)
1446
- for (const column of (Array.isArray(options.columns)
1464
+ this.validateColumns((Array.isArray(options.columns)
1447
1465
  ? options.columns
1448
- : [options.columns]))
1449
- if (column !== "*")
1450
- this.validateName(column);
1466
+ : [options.columns]));
1451
1467
  const tablePath = join(this.databasePath, tableName);
1452
1468
  await this.throwErrorIfTableEmpty(tableName);
1453
1469
  let clonedData = structuredClone(data);
package/dist/utils.d.ts CHANGED
@@ -236,13 +236,17 @@ export declare const createError: (language: ErrorLang, name: ErrorCode, variabl
236
236
  /**
237
237
  * Validates that a string is a safe name for a table, database or column.
238
238
  *
239
- * Names must start with an alphanumeric character (Latin or Arabic), followed
240
- * by alphanumerics (Latin or Arabic), underscores, hyphens or spaces, and be
241
- * at most 255 characters long. Leading or trailing spaces are not allowed.
239
+ * Instead of whitelisting allowed characters, this uses a blacklist that blocks
240
+ * only characters known to cause problems with shell commands, filesystem
241
+ * paths, or injection attacks. All Unicode letters (Latin, Arabic, Chinese,
242
+ * etc.), digits, underscores, hyphens, spaces and forward slashes are allowed.
242
243
  *
243
- * This prevents path traversal (`../`, `..\\`), null-byte injection (`\0`),
244
- * shell injection (`;`, `|`, backticks, `$()`, tabs, newlines, slashes) and
245
- * other reserved-name issues.
244
+ * Blocked characters: null bytes, control characters, `\`, `;`, `|`, `&`,
245
+ * `<`, `>`, `$`, backtick, `!`, `'`, `"`, `{`, `}`, `(`, `)`, `~`, and `.`
246
+ * (reserved as column-path separator). Forward slash (`/`) is intentionally
247
+ * allowed so that nested table names like `"user/logs"` work.
248
+ *
249
+ * Leading or trailing spaces are not allowed.
246
250
  *
247
251
  * @param input - The value to be checked.
248
252
  * @returns boolean - True if the name is safe to use, false otherwise.
package/dist/utils.js CHANGED
@@ -726,13 +726,17 @@ export const createError = (language, name, variable) => {
726
726
  /**
727
727
  * Validates that a string is a safe name for a table, database or column.
728
728
  *
729
- * Names must start with an alphanumeric character (Latin or Arabic), followed
730
- * by alphanumerics (Latin or Arabic), underscores, hyphens or spaces, and be
731
- * at most 255 characters long. Leading or trailing spaces are not allowed.
729
+ * Instead of whitelisting allowed characters, this uses a blacklist that blocks
730
+ * only characters known to cause problems with shell commands, filesystem
731
+ * paths, or injection attacks. All Unicode letters (Latin, Arabic, Chinese,
732
+ * etc.), digits, underscores, hyphens, spaces and forward slashes are allowed.
732
733
  *
733
- * This prevents path traversal (`../`, `..\\`), null-byte injection (`\0`),
734
- * shell injection (`;`, `|`, backticks, `$()`, tabs, newlines, slashes) and
735
- * other reserved-name issues.
734
+ * Blocked characters: null bytes, control characters, `\`, `;`, `|`, `&`,
735
+ * `<`, `>`, `$`, backtick, `!`, `'`, `"`, `{`, `}`, `(`, `)`, `~`, and `.`
736
+ * (reserved as column-path separator). Forward slash (`/`) is intentionally
737
+ * allowed so that nested table names like `"user/logs"` work.
738
+ *
739
+ * Leading or trailing spaces are not allowed.
736
740
  *
737
741
  * @param input - The value to be checked.
738
742
  * @returns boolean - True if the name is safe to use, false otherwise.
@@ -741,12 +745,14 @@ export const isValidName = (input) => typeof input === "string" &&
741
745
  input.length > 0 &&
742
746
  input.length <= 255 &&
743
747
  validNamePattern.test(input);
744
- // Word characters: Latin alphanumerics, underscores, hyphens and Arabic blocks.
745
- const nameWordCharClass = "a-zA-Z0-9_\\u0600-\\u06FF\\u0750-\\u077F\\u08A0-\\u08FF-";
746
- // Leading characters: same as word chars minus underscore and hyphen.
747
- const nameFirstCharClass = "a-zA-Z0-9\\u0600-\\u06FF\\u0750-\\u077F\\u08A0-\\u08FF";
748
- // eslint-disable-next-line no-misleading-character-class
749
- const validNamePattern = new RegExp(`^[${nameFirstCharClass}](?:[${nameWordCharClass} ]*[${nameWordCharClass}])?$`);
748
+ // Forbidden characters: control chars, path separators, shell metacharacters,
749
+ // quoting, grouping, history expansion, home-dir expansion and dot (reserved
750
+ // as column-path separator). The forward slash (/) is intentionally allowed
751
+ // so that nested table names like "user/logs" work.
752
+ const nameForbiddenChars = "\\x00-\\x1F\\x7F.\\\\;|&<>$`!'\\" + "{}()~";
753
+ // Names must be 1-255 chars, start/end with a non-forbidden non-whitespace
754
+ // character, and contain no forbidden characters in between.
755
+ const validNamePattern = new RegExp(`^[^${nameForbiddenChars}\\s\\p{Z}\\p{Cf}][^${nameForbiddenChars}\\p{Cf}]*[^${nameForbiddenChars}\\s\\p{Z}\\p{Cf}]$|^[^${nameForbiddenChars}\\s\\p{Z}\\p{Cf}]$`, "u");
750
756
  /**
751
757
  * Validates that a string is a safe name for a table, database or column and
752
758
  * throws a translated `INVALID_NAME` error if it is not.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "inibase",
3
- "version": "1.6.4",
3
+ "version": "1.6.6",
4
4
  "type": "module",
5
5
  "author": {
6
6
  "name": "Karim Amahtil",