@budibase/backend-core 2.29.13 → 2.29.14

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.
Files changed (38) hide show
  1. package/dist/index.js +2280 -2241
  2. package/dist/index.js.map +4 -4
  3. package/dist/index.js.meta.json +1 -1
  4. package/dist/package.json +4 -4
  5. package/dist/src/db/couch/DatabaseImpl.js +2 -0
  6. package/dist/src/db/couch/DatabaseImpl.js.map +1 -1
  7. package/dist/src/db/couch/index.d.ts +0 -1
  8. package/dist/src/db/couch/index.js +0 -1
  9. package/dist/src/db/couch/index.js.map +1 -1
  10. package/dist/src/environment.d.ts +2 -0
  11. package/dist/src/environment.js +16 -0
  12. package/dist/src/environment.js.map +1 -1
  13. package/dist/src/middleware/errorHandling.js +9 -1
  14. package/dist/src/middleware/errorHandling.js.map +1 -1
  15. package/dist/src/security/secrets.d.ts +1 -0
  16. package/dist/src/security/secrets.js +43 -0
  17. package/dist/src/security/secrets.js.map +1 -0
  18. package/dist/src/sql/sql.js +3 -14
  19. package/dist/src/sql/sql.js.map +1 -1
  20. package/dist/src/sql/utils.d.ts +3 -1
  21. package/dist/src/sql/utils.js +31 -3
  22. package/dist/src/sql/utils.js.map +1 -1
  23. package/dist/tests/core/utilities/jestUtils.d.ts +3 -3
  24. package/dist/tests/core/utilities/jestUtils.js.map +1 -1
  25. package/package.json +4 -4
  26. package/src/db/couch/DatabaseImpl.ts +3 -0
  27. package/src/db/couch/index.ts +0 -1
  28. package/src/environment.ts +17 -0
  29. package/src/middleware/errorHandling.ts +10 -1
  30. package/src/security/secrets.ts +20 -0
  31. package/src/security/tests/secrets.spec.ts +35 -0
  32. package/src/sql/sql.ts +6 -16
  33. package/src/sql/utils.ts +27 -2
  34. package/tests/core/utilities/jestUtils.ts +6 -3
  35. package/dist/src/db/constants.d.ts +0 -1
  36. package/dist/src/db/constants.js +0 -8
  37. package/dist/src/db/constants.js.map +0 -1
  38. package/src/db/constants.ts +0 -5
@@ -13,6 +13,7 @@ import {
13
13
  isDocument,
14
14
  RowResponse,
15
15
  RowValue,
16
+ SqlClient,
16
17
  SQLiteDefinition,
17
18
  SqlQueryBinding,
18
19
  } from "@budibase/types"
@@ -25,6 +26,7 @@ import { SQLITE_DESIGN_DOC_ID } from "../../constants"
25
26
  import { DDInstrumentedDatabase } from "../instrumentation"
26
27
  import { checkSlashesInUrl } from "../../helpers"
27
28
  import env from "../../environment"
29
+ import { sqlLog } from "../../sql/utils"
28
30
 
29
31
  const DATABASE_NOT_FOUND = "Database does not exist."
30
32
 
@@ -322,6 +324,7 @@ export class DatabaseImpl implements Database {
322
324
  ): Promise<T[]> {
323
325
  const dbName = this.name
324
326
  const url = `/${dbName}/${SQLITE_DESIGN_DOC_ID}`
327
+ sqlLog(SqlClient.SQL_LITE, sql, parameters)
325
328
  return await this._sqlQuery<T[]>(url, "POST", {
326
329
  query: sql,
327
330
  args: parameters,
@@ -2,4 +2,3 @@ export * from "./connections"
2
2
  export * from "./DatabaseImpl"
3
3
  export * from "./utils"
4
4
  export { init, getPouch, getPouchDB, closePouchDB } from "./pouchDB"
5
- export * from "../constants"
@@ -205,6 +205,23 @@ const environment = {
205
205
  OPENAI_API_KEY: process.env.OPENAI_API_KEY,
206
206
  }
207
207
 
208
+ type EnvironmentKey = keyof typeof environment
209
+ export const SECRETS: EnvironmentKey[] = [
210
+ "API_ENCRYPTION_KEY",
211
+ "BB_ADMIN_USER_PASSWORD",
212
+ "COUCH_DB_PASSWORD",
213
+ "COUCH_DB_SQL_URL",
214
+ "COUCH_DB_URL",
215
+ "GOOGLE_CLIENT_SECRET",
216
+ "INTERNAL_API_KEY_FALLBACK",
217
+ "INTERNAL_API_KEY",
218
+ "JWT_SECRET",
219
+ "MINIO_ACCESS_KEY",
220
+ "MINIO_SECRET_KEY",
221
+ "OPENAI_API_KEY",
222
+ "REDIS_PASSWORD",
223
+ ]
224
+
208
225
  // clean up any environment variable edge cases
209
226
  for (let [key, value] of Object.entries(environment)) {
210
227
  // handle the edge case of "0" to disable an environment variable
@@ -1,6 +1,7 @@
1
1
  import { APIError } from "@budibase/types"
2
2
  import * as errors from "../errors"
3
3
  import environment from "../environment"
4
+ import { stringContainsSecret } from "../security/secrets"
4
5
 
5
6
  export async function errorHandling(ctx: any, next: any) {
6
7
  try {
@@ -17,11 +18,19 @@ export async function errorHandling(ctx: any, next: any) {
17
18
 
18
19
  let error: APIError = {
19
20
  message: err.message,
20
- status: status,
21
+ status,
21
22
  validationErrors: err.validation,
22
23
  error: errors.getPublicError(err),
23
24
  }
24
25
 
26
+ if (stringContainsSecret(JSON.stringify(error))) {
27
+ error = {
28
+ message: "Unexpected error",
29
+ status,
30
+ error: "Unexpected error",
31
+ }
32
+ }
33
+
25
34
  if (environment.isTest() && ctx.headers["x-budibase-include-stacktrace"]) {
26
35
  // @ts-ignore
27
36
  error.stack = err.stack
@@ -0,0 +1,20 @@
1
+ import environment, { SECRETS } from "../environment"
2
+
3
+ export function stringContainsSecret(str: string) {
4
+ if (str.includes("-----BEGIN PRIVATE KEY-----")) {
5
+ return true
6
+ }
7
+
8
+ for (const key of SECRETS) {
9
+ const value = environment[key]
10
+ if (typeof value !== "string" || value === "") {
11
+ continue
12
+ }
13
+
14
+ if (str.includes(value)) {
15
+ return true
16
+ }
17
+ }
18
+
19
+ return false
20
+ }
@@ -0,0 +1,35 @@
1
+ import { randomUUID } from "crypto"
2
+ import environment, { SECRETS } from "../../environment"
3
+ import { stringContainsSecret } from "../secrets"
4
+
5
+ describe("secrets", () => {
6
+ describe("stringContainsSecret", () => {
7
+ it.each(SECRETS)("detects that a string contains a secret in: %s", key => {
8
+ const needle = randomUUID()
9
+ const haystack = `this is a secret: ${needle}`
10
+ const old = environment[key]
11
+ environment._set(key, needle)
12
+
13
+ try {
14
+ expect(stringContainsSecret(haystack)).toBe(true)
15
+ } finally {
16
+ environment._set(key, old)
17
+ }
18
+ })
19
+
20
+ it.each(SECRETS)(
21
+ "detects that a string does not contain a secret in: %s",
22
+ key => {
23
+ const needle = randomUUID()
24
+ const haystack = `this does not contain a secret`
25
+ const old = environment[key]
26
+ environment._set(key, needle)
27
+ try {
28
+ expect(stringContainsSecret(haystack)).toBe(false)
29
+ } finally {
30
+ environment._set(key, old)
31
+ }
32
+ }
33
+ )
34
+ })
35
+ })
package/src/sql/sql.ts CHANGED
@@ -3,8 +3,10 @@ import * as dbCore from "../db"
3
3
  import {
4
4
  getNativeSql,
5
5
  isExternalTable,
6
- isIsoDateString,
6
+ isValidISODateString,
7
7
  isValidFilter,
8
+ sqlLog,
9
+ isInvalidISODateString,
8
10
  } from "./utils"
9
11
  import { SqlStatements } from "./sqlStatements"
10
12
  import SqlTableQueryBuilder from "./sqlTable"
@@ -38,10 +40,6 @@ const envLimit = environment.SQL_MAX_ROWS
38
40
  : null
39
41
  const BASE_LIMIT = envLimit || 5000
40
42
 
41
- // these are invalid dates sent by the client, need to convert them to a real max date
42
- const MIN_ISO_DATE = "0000-00-00T00:00:00.000Z"
43
- const MAX_ISO_DATE = "9999-00-00T00:00:00.000Z"
44
-
45
43
  function likeKey(client: string, key: string): string {
46
44
  let start: string, end: string
47
45
  switch (client) {
@@ -75,10 +73,10 @@ function parse(input: any) {
75
73
  if (typeof input !== "string") {
76
74
  return input
77
75
  }
78
- if (input === MAX_ISO_DATE || input === MIN_ISO_DATE) {
76
+ if (isInvalidISODateString(input)) {
79
77
  return null
80
78
  }
81
- if (isIsoDateString(input)) {
79
+ if (isValidISODateString(input)) {
82
80
  return new Date(input.trim())
83
81
  }
84
82
  return input
@@ -938,15 +936,7 @@ class SqlQueryBuilder extends SqlTableQueryBuilder {
938
936
  }
939
937
 
940
938
  log(query: string, values?: SqlQueryBinding) {
941
- if (!environment.SQL_LOGGING_ENABLE) {
942
- return
943
- }
944
- const sqlClient = this.getSqlClient()
945
- let string = `[SQL] [${sqlClient.toUpperCase()}] query="${query}"`
946
- if (values) {
947
- string += ` values="${values.join(", ")}"`
948
- }
949
- console.log(string)
939
+ sqlLog(this.getSqlClient(), query, values)
950
940
  }
951
941
  }
952
942
 
package/src/sql/utils.ts CHANGED
@@ -2,10 +2,12 @@ import { DocumentType, SqlQuery, Table, TableSourceType } from "@budibase/types"
2
2
  import { DEFAULT_BB_DATASOURCE_ID } from "../constants"
3
3
  import { Knex } from "knex"
4
4
  import { SEPARATOR } from "../db"
5
+ import environment from "../environment"
5
6
 
6
7
  const DOUBLE_SEPARATOR = `${SEPARATOR}${SEPARATOR}`
7
8
  const ROW_ID_REGEX = /^\[.*]$/g
8
9
  const ENCODED_SPACE = encodeURIComponent(" ")
10
+ const ISO_DATE_REGEX = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$/
9
11
 
10
12
  export function isExternalTableID(tableId: string) {
11
13
  return tableId.startsWith(DocumentType.DATASOURCE + SEPARATOR)
@@ -120,15 +122,38 @@ export function breakRowIdField(_id: string | { _id: string }): any[] {
120
122
  }
121
123
  }
122
124
 
123
- export function isIsoDateString(str: string) {
125
+ export function isInvalidISODateString(str: string) {
124
126
  const trimmedValue = str.trim()
125
- if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$/.test(trimmedValue)) {
127
+ if (!ISO_DATE_REGEX.test(trimmedValue)) {
126
128
  return false
127
129
  }
128
130
  let d = new Date(trimmedValue)
131
+ return isNaN(d.getTime())
132
+ }
133
+
134
+ export function isValidISODateString(str: string) {
135
+ const trimmedValue = str.trim()
136
+ if (!ISO_DATE_REGEX.test(trimmedValue)) {
137
+ return false
138
+ }
139
+ let d = new Date(trimmedValue)
140
+ if (isNaN(d.getTime())) {
141
+ return false
142
+ }
129
143
  return d.toISOString() === trimmedValue
130
144
  }
131
145
 
132
146
  export function isValidFilter(value: any) {
133
147
  return value != null && value !== ""
134
148
  }
149
+
150
+ export function sqlLog(client: string, query: string, values?: any[]) {
151
+ if (!environment.SQL_LOGGING_ENABLE) {
152
+ return
153
+ }
154
+ let string = `[SQL] [${client.toUpperCase()}] query="${query}"`
155
+ if (values) {
156
+ string += ` values="${values.join(", ")}"`
157
+ }
158
+ console.log(string)
159
+ }
@@ -1,4 +1,7 @@
1
- import { db } from "../../../src"
1
+ import {
2
+ CONSTANT_EXTERNAL_ROW_COLS,
3
+ CONSTANT_INTERNAL_ROW_COLS,
4
+ } from "@budibase/shared-core"
2
5
 
3
6
  export function expectFunctionWasCalledTimesWith(
4
7
  jestFunction: any,
@@ -11,7 +14,7 @@ export function expectFunctionWasCalledTimesWith(
11
14
  }
12
15
 
13
16
  export const expectAnyInternalColsAttributes: {
14
- [K in (typeof db.CONSTANT_INTERNAL_ROW_COLS)[number]]: any
17
+ [K in (typeof CONSTANT_INTERNAL_ROW_COLS)[number]]: any
15
18
  } = {
16
19
  tableId: expect.anything(),
17
20
  type: expect.anything(),
@@ -22,7 +25,7 @@ export const expectAnyInternalColsAttributes: {
22
25
  }
23
26
 
24
27
  export const expectAnyExternalColsAttributes: {
25
- [K in (typeof db.CONSTANT_EXTERNAL_ROW_COLS)[number]]: any
28
+ [K in (typeof CONSTANT_EXTERNAL_ROW_COLS)[number]]: any
26
29
  } = {
27
30
  tableId: expect.anything(),
28
31
  _id: expect.anything(),
@@ -1 +0,0 @@
1
- export { CONSTANT_INTERNAL_ROW_COLS, CONSTANT_EXTERNAL_ROW_COLS, isInternalColumnName, } from "@budibase/shared-core";
@@ -1,8 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isInternalColumnName = exports.CONSTANT_EXTERNAL_ROW_COLS = exports.CONSTANT_INTERNAL_ROW_COLS = void 0;
4
- var shared_core_1 = require("@budibase/shared-core");
5
- Object.defineProperty(exports, "CONSTANT_INTERNAL_ROW_COLS", { enumerable: true, get: function () { return shared_core_1.CONSTANT_INTERNAL_ROW_COLS; } });
6
- Object.defineProperty(exports, "CONSTANT_EXTERNAL_ROW_COLS", { enumerable: true, get: function () { return shared_core_1.CONSTANT_EXTERNAL_ROW_COLS; } });
7
- Object.defineProperty(exports, "isInternalColumnName", { enumerable: true, get: function () { return shared_core_1.isInternalColumnName; } });
8
- //# sourceMappingURL=constants.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/db/constants.ts"],"names":[],"mappings":";;;AAAA,qDAI8B;AAH5B,yHAAA,0BAA0B,OAAA;AAC1B,yHAAA,0BAA0B,OAAA;AAC1B,mHAAA,oBAAoB,OAAA"}
@@ -1,5 +0,0 @@
1
- export {
2
- CONSTANT_INTERNAL_ROW_COLS,
3
- CONSTANT_EXTERNAL_ROW_COLS,
4
- isInternalColumnName,
5
- } from "@budibase/shared-core"