mirror of
https://github.com/laurent22/joplin.git
synced 2025-01-02 12:47:41 +02:00
Desktop: Security: Prevent calling arbitrary commands via x-callback-url
This commit is contained in:
parent
0f9727144f
commit
69826610a2
@ -34,7 +34,7 @@ import ShareFolderDialog from '../ShareFolderDialog/ShareFolderDialog';
|
|||||||
import { ShareInvitation } from '@joplin/lib/services/share/reducer';
|
import { ShareInvitation } from '@joplin/lib/services/share/reducer';
|
||||||
import removeKeylessItems from '../ResizableLayout/utils/removeKeylessItems';
|
import removeKeylessItems from '../ResizableLayout/utils/removeKeylessItems';
|
||||||
import { localSyncInfoFromState } from '@joplin/lib/services/synchronizer/syncInfoUtils';
|
import { localSyncInfoFromState } from '@joplin/lib/services/synchronizer/syncInfoUtils';
|
||||||
import { parseCallbackUrl } from '@joplin/lib/callbackUrlUtils';
|
import { isCallbackUrl, parseCallbackUrl } from '@joplin/lib/callbackUrlUtils';
|
||||||
import ElectronAppWrapper from '../../ElectronAppWrapper';
|
import ElectronAppWrapper from '../../ElectronAppWrapper';
|
||||||
import { showMissingMasterKeyMessage } from '@joplin/lib/services/e2ee/utils';
|
import { showMissingMasterKeyMessage } from '@joplin/lib/services/e2ee/utils';
|
||||||
import { MasterKeyEntity } from '@joplin/lib/services/e2ee/types';
|
import { MasterKeyEntity } from '@joplin/lib/services/e2ee/types';
|
||||||
@ -173,6 +173,7 @@ class MainScreenComponent extends React.Component<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private openCallbackUrl(url: string) {
|
private openCallbackUrl(url: string) {
|
||||||
|
if (!isCallbackUrl(url)) throw new Error(`Invalid callback URL: ${url}`);
|
||||||
const { command, params } = parseCallbackUrl(url);
|
const { command, params } = parseCallbackUrl(url);
|
||||||
void CommandService.instance().execute(command.toString(), params.id);
|
void CommandService.instance().execute(command.toString(), params.id);
|
||||||
}
|
}
|
||||||
|
@ -3,13 +3,14 @@ import * as callbackUrlUtils from './callbackUrlUtils';
|
|||||||
describe('callbackUrlUtils', () => {
|
describe('callbackUrlUtils', () => {
|
||||||
|
|
||||||
it('should identify valid callback urls', () => {
|
it('should identify valid callback urls', () => {
|
||||||
const url = 'joplin://x-callback-url/123?a=b';
|
const url = 'joplin://x-callback-url/openFolder?a=b';
|
||||||
expect(callbackUrlUtils.isCallbackUrl(url)).toBe(true);
|
expect(callbackUrlUtils.isCallbackUrl(url)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should identify invalid callback urls', () => {
|
it('should identify invalid callback urls', () => {
|
||||||
expect(callbackUrlUtils.isCallbackUrl('not-joplin://x-callback-url/123?a=b')).toBe(false);
|
expect(callbackUrlUtils.isCallbackUrl('not-joplin://x-callback-url/123?a=b')).toBe(false);
|
||||||
expect(callbackUrlUtils.isCallbackUrl('joplin://xcallbackurl/123?a=b')).toBe(false);
|
expect(callbackUrlUtils.isCallbackUrl('joplin://xcallbackurl/123?a=b')).toBe(false);
|
||||||
|
expect(callbackUrlUtils.isCallbackUrl('joplin://x-callback-url/invalidCommand?a=b')).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should build valid note callback urls', () => {
|
it('should build valid note callback urls', () => {
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
const URL = require('url-parse');
|
const URL = require('url-parse');
|
||||||
|
|
||||||
export function isCallbackUrl(s: string) {
|
export function isCallbackUrl(s: string) {
|
||||||
return s.startsWith('joplin://x-callback-url/');
|
return s.startsWith('joplin://x-callback-url/openNote?') ||
|
||||||
|
s.startsWith('joplin://x-callback-url/openFolder?') ||
|
||||||
|
s.startsWith('joplin://x-callback-url/openTag?');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getNoteCallbackUrl(noteId: string) {
|
export function getNoteCallbackUrl(noteId: string) {
|
||||||
|
Loading…
Reference in New Issue
Block a user