Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import { ListItemConfigurableRepo } from './listItemConfigurableRepo';
import { REPOSITORY_LOCATION_TYPE } from '../../constants/repositoryLocationTypes';
import { getInitialModelRepositoryFormState } from '../state';
import { getInitialModelRepositoryFormOps } from '../interface';
import { act } from 'react-dom/test-utils';
import { TextField } from '@fluentui/react';

describe('ListItemConfigurableRepo', () => {
it('matches snapshot', () => {
Expand Down
9 changes: 5 additions & 4 deletions src/server/serverBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import { EventHubConsumerClient, Subscription, ReceivedEventData, earliestEventPosition } from '@azure/event-hubs';
import { generateDataPlaneRequestBody, generateDataPlaneResponse } from './dataPlaneHelper';
import { convertIotHubToEventHubsConnectionString } from './eventHubHelper';
import { fetchDirectories, findMatchingFile, readFileFromLocal, SAFE_ROOT } from './utils';
import { checkPath, fetchDirectories, findMatchingFile, readFileFromLocal, SAFE_ROOT } from './utils';

export const SERVER_ERROR = 500;
export const SUCCESS = 200;
Expand Down Expand Up @@ -52,7 +52,7 @@
app.post(dataPlaneUri, handleDataPlanePostRequest);
app.post(eventHubMonitorUri, handleEventHubMonitorPostRequest);
app.post(eventHubStopUri, handleEventHubStopPostRequest);
app.get(readFileUri, handleReadFileRequest);

Check failure

Code scanning / CodeQL

Missing rate limiting High

This route handler performs
a file system access
, but is not rate-limited.
app.get(readFileNaiveUri, handleReadFileNaiveRequest);
app.get(getDirectoriesUri, handleGetDirectoriesRequest);

Expand Down Expand Up @@ -89,9 +89,10 @@
res.status(BAD_REQUEST).send();
}
else {
const fileNames = fs.readdirSync(filePath);
const resolvedPath = checkPath(filePath);
const fileNames = fs.readdirSync(resolvedPath);
try {
const foundContent = findMatchingFile(filePath, fileNames, expectedFileName);
const foundContent = findMatchingFile(resolvedPath, fileNames, expectedFileName);
if (foundContent) {
res.status(SUCCESS).send(foundContent);
}
Expand All @@ -100,7 +101,7 @@
}
}
catch (error) {
res.status(NOT_FOUND).send(error.message); // couldn't find matching file, and the folder contains json files that cannot be parsed
res.status(NOT_FOUND).send('Not able to find any matching file.'); // couldn't find matching file, and the folder contains json files that cannot be parsed
}

}
Expand Down Expand Up @@ -141,7 +142,7 @@
}
}
catch (error) {
res.status(SERVER_ERROR).send(error?.message);

Check warning

Code scanning / CodeQL

Exception text reinterpreted as HTML Medium

Exception text
is reinterpreted as HTML without escaping meta-characters.
Exception text
is reinterpreted as HTML without escaping meta-characters.
Exception text
is reinterpreted as HTML without escaping meta-characters.
}
};

Expand Down
21 changes: 11 additions & 10 deletions src/server/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,7 @@ export const SAFE_ROOT = getSafeRoot();

export const fetchDirectories = (dir: string, res: express.Response) => {
try {
// Resolve the requested directory relative to the safe root
const resolvedPath = fs.realpathSync(path.resolve(SAFE_ROOT, path.relative(SAFE_ROOT, dir)));

// Ensure resolvedPath is still inside SAFE_ROOT (prevents traversal attacks)
if (!resolvedPath.startsWith(SAFE_ROOT)) {
return res.status(403).send({ error: "Access denied. Unsafe directory." });
}
const resolvedPath = checkPath(dir);

const result: string[] = [];
for (const item of fs.readdirSync(resolvedPath)) {
Expand Down Expand Up @@ -101,12 +95,19 @@ const isFileExtensionJson = (fileName: string) => {

export const readFileFromLocal = (filePath: string, fileName: string) => {
// Resolve the requested directory relative to the safe root
const resolvedPath = fs.realpathSync(path.resolve(SAFE_ROOT, path.relative(SAFE_ROOT, `${filePath}/${fileName}`)));
const resolvedPath = checkPath(`${filePath}/${fileName}`);

return fs.readFileSync(resolvedPath, 'utf-8');
}

export const checkPath = (filePath: string) => {
// Resolve the requested directory relative to the safe root
const resolvedPath = fs.realpathSync(path.resolve(SAFE_ROOT, path.relative(SAFE_ROOT, filePath)));

// Ensure resolvedPath is still inside SAFE_ROOT (prevents traversal attacks)
if (!resolvedPath.startsWith(SAFE_ROOT)) {
throw new Error("Access denied. Unsafe directory.");
}
return fs.readFileSync(`${filePath}/${fileName}`, 'utf-8');

return resolvedPath;
}