1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-08-24 20:19:10 +02:00

Compare commits

...

2 Commits

Author SHA1 Message Date
Laurent Cozic
5f02af9724 Server v3.3.4 2025-03-03 22:29:46 +00:00
Henry Heino
975f16d21c Server: Security: Improve request validation in default route (#11916) 2025-03-03 22:29:05 +00:00
4 changed files with 43 additions and 5 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/server",
"version": "3.3.3",
"version": "3.3.4",
"private": true,
"scripts": {
"start-dev": "yarn build && JOPLIN_IS_TESTING=1 nodemon --config nodemon.json --ext ts,js,mustache,css,tsx dist/app.js --env dev",

View File

@@ -1,5 +1,6 @@
import { basename } from '@joplin/utils/path';
import { Item } from '../services/database/types';
import { itemIsEncrypted } from './joplinUtils';
import { itemIsEncrypted, localFileFromUrl } from './joplinUtils';
import { expectThrow } from './testing/testUtils';
describe('joplinUtils', () => {
@@ -21,4 +22,21 @@ describe('joplinUtils', () => {
await expectThrow(async () => itemIsEncrypted({ name: 'missing props' }));
});
it.each([
'css/pluginAssets/../../../test',
'css/pluginAssets/../../test',
'js/pluginAssets/./../../test',
])('localFileFromUrl should prevent access to paths outside the assets directory', async (url) => {
await expect(localFileFromUrl(url)).rejects.toThrow('Disallowed access:');
});
it.each([
'css/pluginAssets/test.css',
'js/pluginAssets/subfolder/test.js',
'css/pluginAssets/testing/this-is-a-test.css',
])('localFileFromUrl should allow access to paths inside the assets directory', async (url) => {
const resolvedPath = await localFileFromUrl(url);
// Should resolve to the same file
expect(basename(resolvedPath)).toBe(basename(url));
});
});

View File

@@ -26,6 +26,7 @@ import MustacheService from '../services/MustacheService';
import Logger from '@joplin/utils/Logger';
import config from '../config';
import { TreeItem } from '../models/ItemResourceModel';
import resolvePathWithinDir from '@joplin/lib/utils/resolvePathWithinDir';
const { substrWithEllipsis } = require('@joplin/lib/string-utils');
const logger = Logger.create('JoplinUtils');
@@ -116,11 +117,26 @@ export function isJoplinResourceBlobPath(path: string): boolean {
return path.indexOf(resourceDirName) === 0;
}
export async function localFileFromUrl(url: string): Promise<string> {
const resolveUnsafeAssetPath = (relativeAssetPath: string) => {
const resolvedPath = resolvePathWithinDir(pluginAssetRootDir_, relativeAssetPath);
if (resolvedPath === null) {
throw new ErrorForbidden('Disallowed access: Item is not in the plugin asset directory');
}
return resolvedPath;
};
export async function localFileFromUrl(urlPath: string): Promise<string> {
const cssPluginAssets = 'css/pluginAssets/';
const jsPluginAssets = 'js/pluginAssets/';
if (url.indexOf(cssPluginAssets) === 0) return `${pluginAssetRootDir_}/${url.substr(cssPluginAssets.length)}`;
if (url.indexOf(jsPluginAssets) === 0) return `${pluginAssetRootDir_}/${url.substr(jsPluginAssets.length)}`;
const baseUrls = [cssPluginAssets, jsPluginAssets];
for (const baseUrl of baseUrls) {
if (urlPath.startsWith(baseUrl)) {
const pluginAssetPath = urlPath.substring(baseUrl.length);
return resolveUnsafeAssetPath(pluginAssetPath);
}
}
return null;
}

View File

@@ -1,5 +1,9 @@
# Joplin Server Changelog
## [server-v3.3.4](https://github.com/laurent22/joplin/releases/tag/server-v3.3.4) - 2025-03-03T22:29:29Z
- Security: Improve request validation in default route (#11916 by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
## [server-v3.3.3](https://github.com/laurent22/joplin/releases/tag/server-v3.3.3) - 2025-02-23T19:06:59Z
- Security: Fixed patching user properties (12baa98)