@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.
- package/dist/index.js +2280 -2241
- package/dist/index.js.map +4 -4
- package/dist/index.js.meta.json +1 -1
- package/dist/package.json +4 -4
- package/dist/src/db/couch/DatabaseImpl.js +2 -0
- package/dist/src/db/couch/DatabaseImpl.js.map +1 -1
- package/dist/src/db/couch/index.d.ts +0 -1
- package/dist/src/db/couch/index.js +0 -1
- package/dist/src/db/couch/index.js.map +1 -1
- package/dist/src/environment.d.ts +2 -0
- package/dist/src/environment.js +16 -0
- package/dist/src/environment.js.map +1 -1
- package/dist/src/middleware/errorHandling.js +9 -1
- package/dist/src/middleware/errorHandling.js.map +1 -1
- package/dist/src/security/secrets.d.ts +1 -0
- package/dist/src/security/secrets.js +43 -0
- package/dist/src/security/secrets.js.map +1 -0
- package/dist/src/sql/sql.js +3 -14
- package/dist/src/sql/sql.js.map +1 -1
- package/dist/src/sql/utils.d.ts +3 -1
- package/dist/src/sql/utils.js +31 -3
- package/dist/src/sql/utils.js.map +1 -1
- package/dist/tests/core/utilities/jestUtils.d.ts +3 -3
- package/dist/tests/core/utilities/jestUtils.js.map +1 -1
- package/package.json +4 -4
- package/src/db/couch/DatabaseImpl.ts +3 -0
- package/src/db/couch/index.ts +0 -1
- package/src/environment.ts +17 -0
- package/src/middleware/errorHandling.ts +10 -1
- package/src/security/secrets.ts +20 -0
- package/src/security/tests/secrets.spec.ts +35 -0
- package/src/sql/sql.ts +6 -16
- package/src/sql/utils.ts +27 -2
- package/tests/core/utilities/jestUtils.ts +6 -3
- package/dist/src/db/constants.d.ts +0 -1
- package/dist/src/db/constants.js +0 -8
- package/dist/src/db/constants.js.map +0 -1
- 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,
|
package/src/db/couch/index.ts
CHANGED
package/src/environment.ts
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
|
76
|
+
if (isInvalidISODateString(input)) {
|
|
79
77
|
return null
|
|
80
78
|
}
|
|
81
|
-
if (
|
|
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
|
-
|
|
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
|
|
125
|
+
export function isInvalidISODateString(str: string) {
|
|
124
126
|
const trimmedValue = str.trim()
|
|
125
|
-
if (
|
|
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 {
|
|
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
|
|
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
|
|
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";
|
package/dist/src/db/constants.js
DELETED
|
@@ -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"}
|