diff --git a/packages/components/package.json b/packages/components/package.json index 66e6d6d9381..1874ca10475 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -36,7 +36,7 @@ "@upstash/redis": "^1.22.1", "@zilliz/milvus2-sdk-node": "^2.2.24", "apify-client": "^2.7.1", - "axios": "^0.27.2", + "axios": "1.6.2", "cheerio": "^1.0.0-rc.12", "chromadb": "^1.5.11", "cohere-ai": "^6.2.0", diff --git a/packages/server/package.json b/packages/server/package.json index f2ec5c098d5..a73159995a9 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -47,7 +47,7 @@ "dependencies": { "@oclif/core": "^1.13.10", "async-mutex": "^0.4.0", - "axios": "^0.27.2", + "axios": "1.6.2", "cors": "^2.8.5", "crypto-js": "^4.1.1", "dotenv": "^16.0.0", @@ -61,6 +61,7 @@ "mysql": "^2.18.1", "pg": "^8.11.1", "reflect-metadata": "^0.1.13", + "sanitize-html": "^2.11.0", "socket.io": "^4.6.1", "sqlite3": "^5.1.6", "typeorm": "^0.3.6", @@ -71,6 +72,7 @@ "@types/cors": "^2.8.12", "@types/crypto-js": "^4.1.1", "@types/multer": "^1.4.7", + "@types/sanitize-html": "^2.9.5", "concurrently": "^7.1.0", "nodemon": "^2.0.15", "oclif": "^3", diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index 805a0fec679..2ab454ad746 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -58,6 +58,7 @@ import { CachePool } from './CachePool' import { ICommonObject, IMessage, INodeOptionsValue } from 'flowise-components' import { createRateLimiter, getRateLimiter, initializeRateLimiter } from './utils/rateLimit' import { addAPIKey, compareKeys, deleteAPIKey, getApiKey, getAPIKeys, updateAPIKey } from './utils/apiKey' +import { sanitizeMiddleware } from './utils/XSS' import axios from 'axios' import { Client } from 'langchainhub' import { parsePrompt } from './utils/hub' @@ -118,9 +119,15 @@ export class App { // Allow access from * this.app.use(cors()) + // Switch off the default 'X-Powered-By: Express' header + this.app.disable('x-powered-by') + // Add the expressRequestLogger middleware to log all requests this.app.use(expressRequestLogger) + // Add the sanitizeMiddleware to guard against XSS + this.app.use(sanitizeMiddleware) + if (process.env.FLOWISE_USERNAME && process.env.FLOWISE_PASSWORD) { const username = process.env.FLOWISE_USERNAME const password = process.env.FLOWISE_PASSWORD @@ -967,6 +974,12 @@ export class App { // Download file from assistant this.app.post('/api/v1/openai-assistants-file', async (req: Request, res: Response) => { const filePath = path.join(getUserHome(), '.flowise', 'openai-assistant', req.body.fileName) + //raise error if file path is not absolute + if (!path.isAbsolute(filePath)) return res.status(500).send(`Invalid file path`) + //raise error if file path contains '..' + if (filePath.includes('..')) return res.status(500).send(`Invalid file path`) + //only return from the .flowise openai-assistant folder + if (!(filePath.includes('.flowise') && filePath.includes('openai-assistant'))) return res.status(500).send(`Invalid file path`) res.setHeader('Content-Disposition', 'attachment; filename=' + path.basename(filePath)) const fileStream = fs.createReadStream(filePath) fileStream.pipe(res) diff --git a/packages/server/src/utils/XSS.ts b/packages/server/src/utils/XSS.ts new file mode 100644 index 00000000000..3e96e6c8f54 --- /dev/null +++ b/packages/server/src/utils/XSS.ts @@ -0,0 +1,13 @@ +import { Request, Response, NextFunction } from 'express' +import sanitizeHtml from 'sanitize-html' + +export function sanitizeMiddleware(req: Request, res: Response, next: NextFunction): void { + // decoding is necessary as the url is encoded by the browser + const decodedURI = decodeURI(req.url) + req.url = sanitizeHtml(decodedURI) + for (let p in req.query) { + req.query[p] = sanitizeHtml(req.query[p] as string) + } + + next() +}