fix(security): validate and restrict /api/instructions operation and topic params (CWE-22, CWE-1321) (#986)
This commit is contained in:
@@ -13,6 +13,7 @@ import express, { Request, Response, Application } from 'express';
|
|||||||
import http from 'http';
|
import http from 'http';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
import { ALLOWED_OPERATIONS, ALLOWED_TOPICS } from './allowed-constants.js';
|
||||||
import { logger } from '../../utils/logger.js';
|
import { logger } from '../../utils/logger.js';
|
||||||
import { createMiddleware, summarizeRequestBody, requireLocalhost } from './Middleware.js';
|
import { createMiddleware, summarizeRequestBody, requireLocalhost } from './Middleware.js';
|
||||||
import { errorHandler, notFoundHandler } from './ErrorHandler.js';
|
import { errorHandler, notFoundHandler } from './ErrorHandler.js';
|
||||||
@@ -199,11 +200,25 @@ export class Server {
|
|||||||
const topic = (req.query.topic as string) || 'all';
|
const topic = (req.query.topic as string) || 'all';
|
||||||
const operation = req.query.operation as string | undefined;
|
const operation = req.query.operation as string | undefined;
|
||||||
|
|
||||||
|
// Validate topic
|
||||||
|
if (topic && !ALLOWED_TOPICS.includes(topic)) {
|
||||||
|
return res.status(400).json({ error: 'Invalid topic' });
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let content: string;
|
let content: string;
|
||||||
|
|
||||||
if (operation) {
|
if (operation) {
|
||||||
const operationPath = path.join(__dirname, '../skills/mem-search/operations', `${operation}.md`);
|
// Validate operation
|
||||||
|
if (!ALLOWED_OPERATIONS.includes(operation)) {
|
||||||
|
return res.status(400).json({ error: 'Invalid operation' });
|
||||||
|
}
|
||||||
|
// Path boundary check
|
||||||
|
const OPERATIONS_BASE_DIR = path.resolve(__dirname, '../skills/mem-search/operations');
|
||||||
|
const operationPath = path.resolve(OPERATIONS_BASE_DIR, `${operation}.md`);
|
||||||
|
if (!operationPath.startsWith(OPERATIONS_BASE_DIR + path.sep)) {
|
||||||
|
return res.status(400).json({ error: 'Invalid request' });
|
||||||
|
}
|
||||||
content = await fs.promises.readFile(operationPath, 'utf-8');
|
content = await fs.promises.readFile(operationPath, 'utf-8');
|
||||||
} else {
|
} else {
|
||||||
const skillPath = path.join(__dirname, '../skills/mem-search/SKILL.md');
|
const skillPath = path.join(__dirname, '../skills/mem-search/SKILL.md');
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
// Allowed values for /api/instructions security
|
||||||
|
export const ALLOWED_OPERATIONS = [
|
||||||
|
'search',
|
||||||
|
'context',
|
||||||
|
'summarize',
|
||||||
|
'import',
|
||||||
|
'export'
|
||||||
|
];
|
||||||
|
|
||||||
|
export const ALLOWED_TOPICS = [
|
||||||
|
'workflow',
|
||||||
|
'search_params',
|
||||||
|
'examples',
|
||||||
|
'all'
|
||||||
|
];
|
||||||
Reference in New Issue
Block a user