Merge pull request #14090 from Budibase/budi-8417-check-error-responses-in-middleware-for-environment

Detect secrets in error messages.
This commit is contained in:
Sam Rose 2024-07-04 16:54:03 +01:00 committed by GitHub
commit b7d58e29d2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 83 additions and 2 deletions

@ -1 +1 @@
Subproject commit ff16525b73c5751d344f5c161a682609c0a993f2 Subproject commit b03e584e465f620b49a1b688ff4afc973e6c0758

View File

@ -205,6 +205,23 @@ const environment = {
OPENAI_API_KEY: process.env.OPENAI_API_KEY, OPENAI_API_KEY: process.env.OPENAI_API_KEY,
} }
type EnvironmentKey = keyof typeof environment
export const SECRETS: EnvironmentKey[] = [
"API_ENCRYPTION_KEY",
"BB_ADMIN_USER_PASSWORD",
"COUCH_DB_PASSWORD",
"COUCH_DB_SQL_URL",
"COUCH_DB_URL",
"GOOGLE_CLIENT_SECRET",
"INTERNAL_API_KEY_FALLBACK",
"INTERNAL_API_KEY",
"JWT_SECRET",
"MINIO_ACCESS_KEY",
"MINIO_SECRET_KEY",
"OPENAI_API_KEY",
"REDIS_PASSWORD",
]
// clean up any environment variable edge cases // clean up any environment variable edge cases
for (let [key, value] of Object.entries(environment)) { for (let [key, value] of Object.entries(environment)) {
// handle the edge case of "0" to disable an environment variable // handle the edge case of "0" to disable an environment variable

View File

@ -1,6 +1,7 @@
import { APIError } from "@budibase/types" import { APIError } from "@budibase/types"
import * as errors from "../errors" import * as errors from "../errors"
import environment from "../environment" import environment from "../environment"
import { stringContainsSecret } from "../security/secrets"
export async function errorHandling(ctx: any, next: any) { export async function errorHandling(ctx: any, next: any) {
try { try {
@ -17,11 +18,19 @@ export async function errorHandling(ctx: any, next: any) {
let error: APIError = { let error: APIError = {
message: err.message, message: err.message,
status: status, status,
validationErrors: err.validation, validationErrors: err.validation,
error: errors.getPublicError(err), error: errors.getPublicError(err),
} }
if (stringContainsSecret(JSON.stringify(error))) {
error = {
message: "Unexpected error",
status,
error: "Unexpected error",
}
}
if (environment.isTest() && ctx.headers["x-budibase-include-stacktrace"]) { if (environment.isTest() && ctx.headers["x-budibase-include-stacktrace"]) {
// @ts-ignore // @ts-ignore
error.stack = err.stack error.stack = err.stack

View File

@ -0,0 +1,20 @@
import environment, { SECRETS } from "../environment"
export function stringContainsSecret(str: string) {
if (str.includes("-----BEGIN PRIVATE KEY-----")) {
return true
}
for (const key of SECRETS) {
const value = environment[key]
if (typeof value !== "string" || value === "") {
continue
}
if (str.includes(value)) {
return true
}
}
return false
}

View File

@ -0,0 +1,35 @@
import { randomUUID } from "crypto"
import environment, { SECRETS } from "../../environment"
import { stringContainsSecret } from "../secrets"
describe("secrets", () => {
describe("stringContainsSecret", () => {
it.each(SECRETS)("detects that a string contains a secret in: %s", key => {
const needle = randomUUID()
const haystack = `this is a secret: ${needle}`
const old = environment[key]
environment._set(key, needle)
try {
expect(stringContainsSecret(haystack)).toBe(true)
} finally {
environment._set(key, old)
}
})
it.each(SECRETS)(
"detects that a string does not contain a secret in: %s",
key => {
const needle = randomUUID()
const haystack = `this does not contain a secret`
const old = environment[key]
environment._set(key, needle)
try {
expect(stringContainsSecret(haystack)).toBe(false)
} finally {
environment._set(key, old)
}
}
)
})
})