Detect secrets in error messages.
This commit is contained in:
parent
901428fc9c
commit
253110ac6f
|
@ -205,6 +205,21 @@ const environment = {
|
|||
OPENAI_API_KEY: process.env.OPENAI_API_KEY,
|
||||
}
|
||||
|
||||
type EnvironmentKey = keyof typeof environment
|
||||
export const SECRETS: EnvironmentKey[] = [
|
||||
"API_ENCRYPTION_KEY",
|
||||
"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",
|
||||
"REDIS_PASSWORD",
|
||||
]
|
||||
|
||||
// clean up any environment variable edge cases
|
||||
for (let [key, value] of Object.entries(environment)) {
|
||||
// handle the edge case of "0" to disable an environment variable
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { APIError } from "@budibase/types"
|
||||
import * as errors from "../errors"
|
||||
import environment from "../environment"
|
||||
import { stringContainsSecret } from "../security/secrets"
|
||||
|
||||
export async function errorHandling(ctx: any, next: any) {
|
||||
try {
|
||||
|
@ -17,7 +18,7 @@ export async function errorHandling(ctx: any, next: any) {
|
|||
|
||||
let error: APIError = {
|
||||
message: err.message,
|
||||
status: status,
|
||||
status,
|
||||
validationErrors: err.validation,
|
||||
error: errors.getPublicError(err),
|
||||
}
|
||||
|
@ -27,6 +28,14 @@ export async function errorHandling(ctx: any, next: any) {
|
|||
error.stack = err.stack
|
||||
}
|
||||
|
||||
if (stringContainsSecret(JSON.stringify(error))) {
|
||||
error = {
|
||||
message: "Unexpected error",
|
||||
status,
|
||||
error: "Unexpected error",
|
||||
}
|
||||
}
|
||||
|
||||
ctx.body = error
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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") {
|
||||
continue
|
||||
}
|
||||
|
||||
if (str.includes(value)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
Loading…
Reference in New Issue