Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion packages/server/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ PORT=3000
# DEBUG=true
# LOG_PATH=/your_log_path/.flowise/logs
# LOG_LEVEL=info #(error | warn | info | verbose | debug)
LOG_SANITIZE_BODY_FIELDS=password,pwd,pass,secret,token,apikey,api_key,accesstoken,access_token,refreshtoken,refresh_token,clientsecret,client_secret,privatekey,private_key,secretkey,secret_key,auth,authorization,credential,credentials

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How did you compile this list? How to validate is there any missing field?

LOG_SANITIZE_HEADER_FIELDS=authorization,x-api-key,x-auth-token,cookie

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How this will be deployed in the Flowise cloud?
How this will be communicated to Enterprise customers?

# TOOL_FUNCTION_BUILTIN_DEP=crypto,fs
# TOOL_FUNCTION_EXTERNAL_DEP=moment,lodash
# ALLOW_BUILTIN_DEP=false
Expand Down Expand Up @@ -180,4 +182,4 @@ JWT_REFRESH_TOKEN_EXPIRY_IN_MINUTES=43200
############################################################################################################

# PUPPETEER_EXECUTABLE_FILE_PATH='C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe'
# PLAYWRIGHT_EXECUTABLE_FILE_PATH='C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe'
# PLAYWRIGHT_EXECUTABLE_FILE_PATH='C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe'
56 changes: 47 additions & 9 deletions packages/server/src/utils/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,25 +193,63 @@ requestLogger = createLogger({
]
})

function getSensitiveBodyFields(): string[] {
return (process.env.LOG_SANITIZE_BODY_FIELDS as string)
.toLowerCase()
.split(',')
.map((f) => f.trim())
}

function getSensitiveHeaderFields(): string[] {
return (process.env.LOG_SANITIZE_HEADER_FIELDS as string)
.toLowerCase()
.split(',')
.map((f) => f.trim())
}

function sanitizeObject(obj: any): any {
if (!obj || typeof obj !== 'object') return obj

const sensitiveFields = getSensitiveBodyFields()
const sanitized = Array.isArray(obj) ? [...obj] : { ...obj }
Object.keys(sanitized).forEach((key) => {
const lowerKey = key.toLowerCase()
if (sensitiveFields.includes(lowerKey)) {
sanitized[key] = '********'
} else if (typeof sanitized[key] === 'string') {
if (sanitized[key].includes('@') && sanitized[key].includes('.')) {
sanitized[key] = sanitized[key].replace(/([^@\s]+)@([^@\s]+)/g, '**********')
}
}
})

return sanitized
}

export function expressRequestLogger(req: Request, res: Response, next: NextFunction): void {
const unwantedLogURLs = ['/api/v1/node-icon/', '/api/v1/components-credentials-icon/', '/api/v1/ping']

if (/\/api\/v1\//i.test(req.url) && !unwantedLogURLs.some((url) => new RegExp(url, 'i').test(req.url))) {
// Create a sanitized copy of the request body
const sanitizedBody = { ...req.body }
if (sanitizedBody.password) {
sanitizedBody.password = '********'
}
const sanitizedBody = sanitizeObject(req.body)

const sanitizedQuery = sanitizeObject(req.query)
const sanitizedHeaders = { ...req.headers }

const sensitiveHeaders = getSensitiveHeaderFields()
sensitiveHeaders.forEach((header) => {
if (sanitizedHeaders[header]) {
sanitizedHeaders[header] = '********'
}
})

// Use the shared requestLogger with request-specific metadata
const requestMetadata = {
request: {
method: req.method,
url: req.url,
body: sanitizedBody, // Use sanitized body instead of raw body
query: req.query,
body: sanitizedBody,
query: sanitizedQuery,
params: req.params,
headers: req.headers
headers: sanitizedHeaders
}
}
Comment on lines 245 to 254
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even with redaction, logging HTTP request bodies, params and query strings is generally a bad idea because it significantly increases the risk of accidental exposure of sensitive data due to misconfigurations, broken redaction logic, or logs being improperly secured. It also creates a massive compliance burden under regulations like GDPR and CCPA, as these logs can inadvertently capture Personally Identifiable Information that must then be managed and defensibly deleted.

Recommend either removing body, params & querystring from here or putting them behind a log level so they're only captured when debug logs are enabled.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btw, apologies if this changes the requirement from what's on the Jira. The Jira's wording was based on the documented findings. Now that I'm seeing the code and not just the finding, I think the finding's recommendation was not in line with how Workday generally treats areas where sensitive data could slip in. Omitting these specific bits on production request logging is standard practice.


Expand Down