mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-24 10:27:10 +02:00
Merge branch 'release-2.12' into dev
This commit is contained in:
commit
13b7e3657b
@ -133,6 +133,7 @@ packages/app-desktop/gui/ClipperConfigScreen.js
|
||||
packages/app-desktop/gui/ConfigScreen/ButtonBar.js
|
||||
packages/app-desktop/gui/ConfigScreen/ConfigScreen.js
|
||||
packages/app-desktop/gui/ConfigScreen/Sidebar.js
|
||||
packages/app-desktop/gui/ConfigScreen/controls/MissingPasswordHelpLink.js
|
||||
packages/app-desktop/gui/ConfigScreen/controls/ToggleAdvancedSettingsButton.js
|
||||
packages/app-desktop/gui/ConfigScreen/controls/plugins/PluginBox.js
|
||||
packages/app-desktop/gui/ConfigScreen/controls/plugins/PluginsStates.js
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -119,6 +119,7 @@ packages/app-desktop/gui/ClipperConfigScreen.js
|
||||
packages/app-desktop/gui/ConfigScreen/ButtonBar.js
|
||||
packages/app-desktop/gui/ConfigScreen/ConfigScreen.js
|
||||
packages/app-desktop/gui/ConfigScreen/Sidebar.js
|
||||
packages/app-desktop/gui/ConfigScreen/controls/MissingPasswordHelpLink.js
|
||||
packages/app-desktop/gui/ConfigScreen/controls/ToggleAdvancedSettingsButton.js
|
||||
packages/app-desktop/gui/ConfigScreen/controls/plugins/PluginBox.js
|
||||
packages/app-desktop/gui/ConfigScreen/controls/plugins/PluginsStates.js
|
||||
|
@ -35,7 +35,7 @@
|
||||
],
|
||||
"owner": "Laurent Cozic"
|
||||
},
|
||||
"version": "2.12.0",
|
||||
"version": "2.12.1",
|
||||
"bin": "./main.js",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
|
@ -21,8 +21,7 @@ import getDefaultPluginsInfo from '@joplin/lib/services/plugins/defaultPlugins/d
|
||||
import JoplinCloudConfigScreen from '../JoplinCloudConfigScreen';
|
||||
import ToggleAdvancedSettingsButton from './controls/ToggleAdvancedSettingsButton';
|
||||
import shouldShowMissingPasswordWarning from '@joplin/lib/components/shared/config/shouldShowMissingPasswordWarning';
|
||||
import shim from '@joplin/lib/shim';
|
||||
import StyledLink from '../style/StyledLink';
|
||||
import MacOSMissingPasswordHelpLink from './controls/MissingPasswordHelpLink';
|
||||
const { KeymapConfigScreen } = require('../KeymapConfig/KeymapConfigScreen');
|
||||
|
||||
const settingKeyToControl: any = {
|
||||
@ -190,25 +189,14 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
||||
// saved yet).
|
||||
const matchesSavedTarget = settings['sync.target'] === this.props.settings['sync.target'];
|
||||
if (matchesSavedTarget && shouldShowMissingPasswordWarning(settings['sync.target'], settings)) {
|
||||
const openMissingPasswordFAQ = () =>
|
||||
bridge().openExternal('https://joplinapp.org/faq#why-did-my-sync-and-encryption-passwords-disappear-after-updating-joplin');
|
||||
|
||||
const macInfoLink = (
|
||||
<StyledLink href="#"
|
||||
onClick={openMissingPasswordFAQ}
|
||||
style={theme.linkStyle}
|
||||
>
|
||||
{_('Help')}
|
||||
</StyledLink>
|
||||
);
|
||||
|
||||
// The FAQ section related to missing passwords is specific to MacOS/ARM -- only show it
|
||||
// in that case.
|
||||
const showMacInfoLink = shim.isMac() && process.arch === 'arm64';
|
||||
|
||||
settingComps.push(
|
||||
<p key='missing-password-warning' style={warningStyle}>
|
||||
{_('Warning: Missing password.')}{' '}{showMacInfoLink ? macInfoLink : null}
|
||||
{_('%s: Missing password.', _('Warning'))}
|
||||
{' '}
|
||||
<MacOSMissingPasswordHelpLink
|
||||
theme={theme}
|
||||
text={_('Help')}
|
||||
/>
|
||||
</p>,
|
||||
);
|
||||
}
|
||||
|
@ -0,0 +1,35 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import shim from '@joplin/lib/shim';
|
||||
import bridge from '../../../services/bridge';
|
||||
import StyledLink from '../../style/StyledLink';
|
||||
|
||||
interface Props {
|
||||
theme: any;
|
||||
text: string;
|
||||
}
|
||||
|
||||
const openMissingPasswordFAQ = () =>
|
||||
bridge().openExternal('https://joplinapp.org/faq#why-did-my-sync-and-encryption-passwords-disappear-after-updating-joplin');
|
||||
|
||||
// A link to a specific part of the FAQ related to passwords being cleared when upgrading
|
||||
// to a MacOS/ARM release.
|
||||
const MacOSMissingPasswordHelpLink: React.FunctionComponent<Props> = props => {
|
||||
const macInfoLink = (
|
||||
<StyledLink href="#"
|
||||
onClick={openMissingPasswordFAQ}
|
||||
style={props.theme.linkStyle}
|
||||
>
|
||||
{props.text}
|
||||
</StyledLink>
|
||||
);
|
||||
|
||||
// The FAQ section related to missing passwords is specific to MacOS/ARM -- only show it
|
||||
// in that case.
|
||||
const newArchitectureReleasedRecently = Date.now() <= Date.UTC(2023, 11); // 11 = December
|
||||
const showMacInfoLink = shim.isMac() && process.arch === 'arm64' && newArchitectureReleasedRecently;
|
||||
|
||||
return showMacInfoLink ? macInfoLink : null;
|
||||
};
|
||||
|
||||
export default MacOSMissingPasswordHelpLink;
|
@ -17,6 +17,7 @@ import Setting from '@joplin/lib/models/Setting';
|
||||
import CommandService from '@joplin/lib/services/CommandService';
|
||||
import { PublicPrivateKeyPair } from '@joplin/lib/services/e2ee/ppk';
|
||||
import ToggleAdvancedSettingsButton from '../ConfigScreen/controls/ToggleAdvancedSettingsButton';
|
||||
import MacOSMissingPasswordHelpLink from '../ConfigScreen/controls/MissingPasswordHelpLink';
|
||||
|
||||
interface Props {
|
||||
themeId: any;
|
||||
@ -252,7 +253,16 @@ const EncryptionConfigScreen = (props: Props) => {
|
||||
const buttonTitle = CommandService.instance().label('openMasterPasswordDialog');
|
||||
|
||||
const needPasswordMessage = !needMasterPassword ? null : (
|
||||
<p className="needpassword">{_('Your password is needed to decrypt some of your data.')}<br/>{_('Please click on "%s" to proceed, or set the passwords in the "%s" list below.', buttonTitle, _('Encryption keys'))}</p>
|
||||
<p className="needpassword">
|
||||
{_('Your password is needed to decrypt some of your data.')}
|
||||
<br/>
|
||||
{_('Please click on "%s" to proceed, or set the passwords in the "%s" list below.', buttonTitle, _('Encryption keys'))}
|
||||
<br/>
|
||||
<MacOSMissingPasswordHelpLink
|
||||
theme={theme}
|
||||
text={_('%s: Missing password', _('Help'))}
|
||||
/>
|
||||
</p>
|
||||
);
|
||||
|
||||
return (
|
||||
|
@ -3,7 +3,8 @@ import shim from '@joplin/lib/shim';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import bridge from '../../../services/bridge';
|
||||
import { openItemById } from '../../NoteEditor/utils/contextMenu';
|
||||
const { parseResourceUrl, urlProtocol, fileUriToPath } = require('@joplin/lib/urlUtils');
|
||||
const { parseResourceUrl, urlProtocol } = require('@joplin/lib/urlUtils');
|
||||
import { fileUriToPath } from '@joplin/utils/url';
|
||||
const { urlDecode } = require('@joplin/lib/string-utils');
|
||||
|
||||
export const declaration: CommandDeclaration = {
|
||||
|
@ -1,10 +1,15 @@
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
import { processPastedHtml } from './resourceHandling';
|
||||
|
||||
describe('resourceHandling', () => {
|
||||
it('should sanitize pasted HTML', async () => {
|
||||
Setting.setConstant('resourceDir', '/home/.config/joplin/resources');
|
||||
|
||||
const testCases = [
|
||||
['Test: <style onload="evil()"></style>', 'Test: <style></style>'],
|
||||
['<a href="javascript: alert()">test</a>', '<a href="#">test</a>'],
|
||||
['<a href="file:///home/.config/joplin/resources/test.pdf">test</a>', '<a href="file:///home/.config/joplin/resources/test.pdf">test</a>'],
|
||||
['<a href="file:///etc/passwd">evil.pdf</a>', '<a href="#">evil.pdf</a>'],
|
||||
['<script >evil()</script>', ''],
|
||||
['<script>evil()</script>', ''],
|
||||
[
|
||||
|
@ -8,7 +8,7 @@ import ResourceFetcher from '@joplin/lib/services/ResourceFetcher';
|
||||
import htmlUtils from '@joplin/lib/htmlUtils';
|
||||
import rendererHtmlUtils, { extractHtmlBody } from '@joplin/renderer/htmlUtils';
|
||||
import Logger from '@joplin/utils/Logger';
|
||||
const { fileUriToPath } = require('@joplin/lib/urlUtils');
|
||||
import { fileUriToPath } from '@joplin/utils/url';
|
||||
const joplinRendererUtils = require('@joplin/renderer').utils;
|
||||
const { clipboard } = require('electron');
|
||||
const mimeUtils = require('@joplin/lib/mime-utils.js').mime;
|
||||
@ -179,6 +179,8 @@ export async function processPastedHtml(html: string) {
|
||||
return extractHtmlBody(rendererHtmlUtils.sanitizeHtml(
|
||||
htmlUtils.replaceImageUrls(html, (src: string) => {
|
||||
return mappedResources[src];
|
||||
}),
|
||||
}), {
|
||||
allowedFilePrefixes: [Setting.value('resourceDir')],
|
||||
},
|
||||
));
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/app-desktop",
|
||||
"version": "2.12.11",
|
||||
"version": "2.12.14",
|
||||
"description": "Joplin for Desktop",
|
||||
"main": "main.js",
|
||||
"private": true,
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@joplin/fork-htmlparser2",
|
||||
"description": "Fast & forgiving HTML/XML/RSS parser",
|
||||
"version": "4.1.45",
|
||||
"version": "4.1.46",
|
||||
"author": "Felix Boehm <me@feedic.com>",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
@ -2,7 +2,7 @@
|
||||
"name": "@joplin/fork-sax",
|
||||
"description": "An evented streaming XML parser in JavaScript",
|
||||
"author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me/)",
|
||||
"version": "1.2.49",
|
||||
"version": "1.2.50",
|
||||
"main": "lib/sax.js",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/fork-uslug",
|
||||
"version": "1.0.10",
|
||||
"version": "1.0.11",
|
||||
"description": "A permissive slug generator that works with unicode.",
|
||||
"author": "Jeremy Selier <jerem.selier@gmail.com>",
|
||||
"publishConfig": {
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"name": "generate-plugin-doc",
|
||||
"packageManager": "yarn@3.6.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"buildPluginDoc_": "typedoc --name 'Joplin Plugin API Documentation' --mode file -theme '../../Assets/PluginDocTheme/' --readme '../../Assets/PluginDocTheme/index.md' --excludeNotExported --excludeExternals --excludePrivate --excludeProtected --out ../../../joplin-website/docs/api/references/plugin_api ../lib/services/plugins/api/"
|
||||
},
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/htmlpack",
|
||||
"version": "2.12.0",
|
||||
"version": "2.12.1",
|
||||
"description": "Pack an HTML file and all its linked resources into a single HTML file",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.ts",
|
||||
@ -14,7 +14,7 @@
|
||||
"author": "Laurent Cozic",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@joplin/fork-htmlparser2": "^4.1.45",
|
||||
"@joplin/fork-htmlparser2": "^4.1.46",
|
||||
"css": "3.0.0",
|
||||
"datauri": "4.1.0",
|
||||
"fs-extra": "11.1.1",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/lib",
|
||||
"version": "2.12.0",
|
||||
"version": "2.12.1",
|
||||
"description": "Joplin Core library",
|
||||
"author": "Laurent Cozic",
|
||||
"homepage": "",
|
||||
@ -31,14 +31,14 @@
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "3.296.0",
|
||||
"@aws-sdk/s3-request-presigner": "3.296.0",
|
||||
"@joplin/fork-htmlparser2": "^4.1.44",
|
||||
"@joplin/fork-sax": "^1.2.48",
|
||||
"@joplin/fork-uslug": "^1.0.9",
|
||||
"@joplin/htmlpack": "~2.12",
|
||||
"@joplin/renderer": "~2.12",
|
||||
"@joplin/turndown": "^4.0.66",
|
||||
"@joplin/turndown-plugin-gfm": "^1.0.48",
|
||||
"@joplin/utils": "~2.12",
|
||||
"@joplin/fork-htmlparser2": "^4.1.46",
|
||||
"@joplin/fork-sax": "^1.2.50",
|
||||
"@joplin/fork-uslug": "^1.0.11",
|
||||
"@joplin/htmlpack": "^2.12.1",
|
||||
"@joplin/renderer": "^2.12.1",
|
||||
"@joplin/turndown": "^4.0.68",
|
||||
"@joplin/turndown-plugin-gfm": "^1.0.50",
|
||||
"@joplin/utils": "^2.12.1",
|
||||
"@types/nanoid": "3.0.0",
|
||||
"async-mutex": "0.4.0",
|
||||
"base-64": "1.0.0",
|
||||
|
@ -24,9 +24,9 @@ import * as ArrayUtils from '../../../ArrayUtils';
|
||||
import Logger from '@joplin/utils/Logger';
|
||||
const { mimeTypeFromHeaders } = require('../../../net-utils');
|
||||
const { fileExtension, safeFileExtension, safeFilename, filename } = require('../../../path-utils');
|
||||
const { fileUriToPath } = require('../../../urlUtils');
|
||||
const { MarkupToHtml } = require('@joplin/renderer');
|
||||
const { ErrorNotFound } = require('../utils/errors');
|
||||
import { fileUriToPath } from '@joplin/utils/url';
|
||||
|
||||
const logger = Logger.create('routes/notes');
|
||||
|
||||
|
@ -105,101 +105,4 @@ urlUtils.objectToQueryString = function(query) {
|
||||
return queryString;
|
||||
};
|
||||
|
||||
// This is a modified version of the file-uri-to-path package:
|
||||
//
|
||||
// - It removes the dependency to the "path" package, which wouldn't work with
|
||||
// React Native.
|
||||
//
|
||||
// - It always returns paths with forward slashes "/". This is normally handled
|
||||
// properly everywhere.
|
||||
//
|
||||
// - Adds the "platform" parameter to optionall return paths with "\" for win32
|
||||
function fileUriToPath_(uri, platform) {
|
||||
const sep = '/';
|
||||
|
||||
if (
|
||||
typeof uri !== 'string' ||
|
||||
uri.length <= 7 ||
|
||||
uri.substring(0, 7) !== 'file://'
|
||||
) {
|
||||
throw new TypeError(
|
||||
'must pass in a file:// URI to convert to a file path',
|
||||
);
|
||||
}
|
||||
|
||||
const rest = decodeURI(uri.substring(7));
|
||||
const firstSlash = rest.indexOf('/');
|
||||
let host = rest.substring(0, firstSlash);
|
||||
let path = rest.substring(firstSlash + 1);
|
||||
|
||||
// 2. Scheme Definition
|
||||
// As a special case, <host> can be the string "localhost" or the empty
|
||||
// string; this is interpreted as "the machine from which the URL is
|
||||
// being interpreted".
|
||||
if (host === 'localhost') {
|
||||
host = '';
|
||||
}
|
||||
|
||||
if (host) {
|
||||
host = sep + sep + host;
|
||||
}
|
||||
|
||||
// 3.2 Drives, drive letters, mount points, file system root
|
||||
// Drive letters are mapped into the top of a file URI in various ways,
|
||||
// depending on the implementation; some applications substitute
|
||||
// vertical bar ("|") for the colon after the drive letter, yielding
|
||||
// "file:///c|/tmp/test.txt". In some cases, the colon is left
|
||||
// unchanged, as in "file:///c:/tmp/test.txt". In other cases, the
|
||||
// colon is simply omitted, as in "file:///c/tmp/test.txt".
|
||||
path = path.replace(/^(.+)\|/, '$1:');
|
||||
|
||||
// for Windows, we need to invert the path separators from what a URI uses
|
||||
// if (sep === '\\') {
|
||||
// path = path.replace(/\//g, '\\');
|
||||
// }
|
||||
|
||||
if (/^.+:/.test(path)) {
|
||||
// has Windows drive at beginning of path
|
||||
} else {
|
||||
// unix path…
|
||||
path = sep + path;
|
||||
}
|
||||
|
||||
if (platform === 'win32') {
|
||||
return (host + path).replace(/\//g, '\\');
|
||||
} else {
|
||||
return host + path;
|
||||
}
|
||||
}
|
||||
|
||||
urlUtils.fileUriToPath = (path, platform = 'linux') => {
|
||||
const output = fileUriToPath_(path, platform);
|
||||
|
||||
// The file-uri-to-path module converts Windows path such as
|
||||
//
|
||||
// file://c:/autoexec.bat => \\c:\autoexec.bat
|
||||
//
|
||||
// Probably because a file:// that starts with only two slashes is not
|
||||
// quite valid. If we use three slashes, it works:
|
||||
//
|
||||
// file:///c:/autoexec.bat => c:\autoexec.bat
|
||||
//
|
||||
// However there are various places in the app where we can find
|
||||
// paths with only two slashes because paths are often constructed
|
||||
// as `file://${resourcePath}` - which works in all OSes except
|
||||
// Windows.
|
||||
//
|
||||
// So here we introduce a special case - if we detect that we have
|
||||
// an invalid Windows path that starts with \\x:, we just remove
|
||||
// the first two backslashes.
|
||||
//
|
||||
// https://github.com/laurent22/joplin/issues/5693
|
||||
|
||||
if (output.match(/^\/\/[a-zA-Z]:/)) {
|
||||
return output.substr(2);
|
||||
}
|
||||
|
||||
return output;
|
||||
};
|
||||
|
||||
module.exports = urlUtils;
|
||||
|
@ -71,30 +71,4 @@ describe('urlUtils', () => {
|
||||
}
|
||||
}));
|
||||
|
||||
it('should convert a file URI to a file path', (async () => {
|
||||
// Tests imported from https://github.com/TooTallNate/file-uri-to-path/tree/master/test
|
||||
const testCases = {
|
||||
'file://host/path': '//host/path',
|
||||
'file://localhost/etc/fstab': '/etc/fstab',
|
||||
'file:///etc/fstab': '/etc/fstab',
|
||||
'file:///c:/WINDOWS/clock.avi': 'c:/WINDOWS/clock.avi',
|
||||
'file://localhost/c|/WINDOWS/clock.avi': 'c:/WINDOWS/clock.avi',
|
||||
'file:///c|/WINDOWS/clock.avi': 'c:/WINDOWS/clock.avi',
|
||||
'file://localhost/c:/WINDOWS/clock.avi': 'c:/WINDOWS/clock.avi',
|
||||
'file://hostname/path/to/the%20file.txt': '//hostname/path/to/the file.txt',
|
||||
'file:///c:/path/to/the%20file.txt': 'c:/path/to/the file.txt',
|
||||
'file:///C:/Documents%20and%20Settings/davris/FileSchemeURIs.doc': 'C:/Documents and Settings/davris/FileSchemeURIs.doc',
|
||||
'file:///C:/caf%C3%A9/%C3%A5r/d%C3%BCnn/%E7%89%9B%E9%93%83/Ph%E1%BB%9F/%F0%9F%98%B5.exe': 'C:/café/år/dünn/牛铃/Phở/😵.exe',
|
||||
};
|
||||
|
||||
for (const [input, expected] of Object.entries(testCases)) {
|
||||
const actual = urlUtils.fileUriToPath(input);
|
||||
expect(actual).toBe(expected);
|
||||
}
|
||||
|
||||
expect(urlUtils.fileUriToPath('file://c:/not/quite/right')).toBe('c:/not/quite/right');
|
||||
expect(urlUtils.fileUriToPath('file:///d:/better')).toBe('d:/better');
|
||||
expect(urlUtils.fileUriToPath('file:///c:/AUTOEXEC.BAT', 'win32')).toBe('c:\\AUTOEXEC.BAT');
|
||||
}));
|
||||
|
||||
});
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/plugin-repo-cli",
|
||||
"version": "2.12.0",
|
||||
"version": "2.12.1",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"bin": "./dist/index.js",
|
||||
@ -18,9 +18,9 @@
|
||||
"author": "",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@joplin/lib": "~2.12",
|
||||
"@joplin/tools": "~2.12",
|
||||
"@joplin/utils": "~2.12",
|
||||
"@joplin/lib": "^2.12.1",
|
||||
"@joplin/tools": "^2.12.1",
|
||||
"@joplin/utils": "^2.12.1",
|
||||
"fs-extra": "11.1.1",
|
||||
"gh-release-assets": "2.0.1",
|
||||
"node-fetch": "2.6.7",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/react-native-saf-x",
|
||||
"version": "2.12.0",
|
||||
"version": "2.12.1",
|
||||
"description": "a module to help work with scoped storages on android easily",
|
||||
"main": "src/index",
|
||||
"react-native": "src/index",
|
||||
|
@ -1,5 +1,6 @@
|
||||
const Entities = require('html-entities').AllHtmlEntities;
|
||||
const htmlentities = new Entities().encode;
|
||||
import { fileUriToPath } from '@joplin/utils/url';
|
||||
const htmlparser2 = require('@joplin/fork-htmlparser2');
|
||||
|
||||
// [\s\S] instead of . for multiline matching
|
||||
@ -31,7 +32,8 @@ const selfClosingElements = [
|
||||
];
|
||||
|
||||
interface SanitizeHtmlOptions {
|
||||
addNoMdConvClass: boolean;
|
||||
addNoMdConvClass?: boolean;
|
||||
allowedFilePrefixes?: string[];
|
||||
}
|
||||
|
||||
export const attributesHtml = (attr: Record<string, string>) => {
|
||||
@ -157,20 +159,36 @@ class HtmlUtils {
|
||||
.replace(/</g, '<');
|
||||
}
|
||||
|
||||
private isAcceptedUrl(url: string): boolean {
|
||||
private isAcceptedUrl(url: string, allowedFilePrefixes: string[]): boolean {
|
||||
url = url.toLowerCase();
|
||||
return url.startsWith('https://') ||
|
||||
if (url.startsWith('https://') ||
|
||||
url.startsWith('http://') ||
|
||||
url.startsWith('mailto://') ||
|
||||
// We also allow anchors but only with a specific set of a characters.
|
||||
// Fixes https://github.com/laurent22/joplin/issues/8286
|
||||
!!url.match(/^#[a-zA-Z0-9-]+$/);
|
||||
!!url.match(/^#[a-zA-Z0-9-]+$/)) return true;
|
||||
|
||||
if (url.startsWith('file://')) {
|
||||
// We need to do a case insensitive comparison because the URL we
|
||||
// get appears to be converted to lowercase somewhere. To be
|
||||
// completely sure, we make it lowercase explicitely.
|
||||
const filePath = fileUriToPath(url).toLowerCase();
|
||||
for (const filePrefix of allowedFilePrefixes) {
|
||||
if (filePath.startsWith(filePrefix.toLowerCase())) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public sanitizeHtml(html: string, options: SanitizeHtmlOptions = null) {
|
||||
options = { // If true, adds a "jop-noMdConv" class to all the tags.
|
||||
options = {
|
||||
// If true, adds a "jop-noMdConv" class to all the tags.
|
||||
// It can be used afterwards to restore HTML tags in Markdown.
|
||||
addNoMdConvClass: false, ...options };
|
||||
addNoMdConvClass: false,
|
||||
allowedFilePrefixes: [],
|
||||
...options,
|
||||
};
|
||||
|
||||
const output: string[] = [];
|
||||
|
||||
@ -247,7 +265,7 @@ class HtmlUtils {
|
||||
// particular we want to exclude `javascript:` URLs. This
|
||||
// applies to A tags, and also AREA ones but to be safe we don't
|
||||
// filter on the tag name and process all HREF attributes.
|
||||
if ('href' in attrs && !this.isAcceptedUrl(attrs['href'])) {
|
||||
if ('href' in attrs && !this.isAcceptedUrl(attrs['href'], options.allowedFilePrefixes)) {
|
||||
attrs['href'] = '#';
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/renderer",
|
||||
"version": "2.12.0",
|
||||
"version": "2.12.1",
|
||||
"description": "The Joplin note renderer, used the mobile and desktop application",
|
||||
"repository": "https://github.com/laurent22/joplin/tree/dev/packages/renderer",
|
||||
"main": "index.js",
|
||||
@ -26,8 +26,9 @@
|
||||
"typescript": "5.1.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@joplin/fork-htmlparser2": "^4.1.44",
|
||||
"@joplin/fork-uslug": "^1.0.9",
|
||||
"@joplin/fork-htmlparser2": "^4.1.46",
|
||||
"@joplin/fork-uslug": "^1.0.11",
|
||||
"@joplin/utils": "~2.12",
|
||||
"font-awesome-filetypes": "2.1.0",
|
||||
"fs-extra": "11.1.1",
|
||||
"highlight.js": "11.8.0",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/tools",
|
||||
"version": "2.12.0",
|
||||
"version": "2.12.1",
|
||||
"description": "Various tools for Joplin",
|
||||
"main": "index.js",
|
||||
"author": "Laurent Cozic",
|
||||
@ -20,9 +20,9 @@
|
||||
},
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@joplin/lib": "~2.12",
|
||||
"@joplin/renderer": "~2.12",
|
||||
"@joplin/utils": "~2.12",
|
||||
"@joplin/lib": "^2.12.1",
|
||||
"@joplin/renderer": "^2.12.1",
|
||||
"@joplin/utils": "^2.12.1",
|
||||
"compare-versions": "3.6.0",
|
||||
"dayjs": "1.11.9",
|
||||
"execa": "4.1.0",
|
||||
@ -42,7 +42,7 @@
|
||||
"yargs": "17.7.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@joplin/fork-htmlparser2": "^4.1.44",
|
||||
"@joplin/fork-htmlparser2": "^4.1.46",
|
||||
"@rmp135/sql-ts": "1.18.0",
|
||||
"@types/fs-extra": "11.0.1",
|
||||
"@types/jest": "29.5.3",
|
||||
|
@ -4,7 +4,7 @@
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"version": "1.0.49",
|
||||
"version": "1.0.50",
|
||||
"author": "Dom Christie",
|
||||
"main": "lib/turndown-plugin-gfm.cjs.js",
|
||||
"devDependencies": {
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@joplin/turndown",
|
||||
"description": "A library that converts HTML to Markdown",
|
||||
"version": "4.0.67",
|
||||
"version": "4.0.68",
|
||||
"author": "Dom Christie",
|
||||
"main": "lib/turndown.cjs.js",
|
||||
"publishConfig": {
|
||||
|
@ -1,16 +1,17 @@
|
||||
{
|
||||
"name": "@joplin/utils",
|
||||
"version": "2.12.0",
|
||||
"version": "2.12.1",
|
||||
"description": "Utilities for Joplin",
|
||||
"repository": "https://github.com/laurent22/joplin/tree/dev/packages/utils",
|
||||
"exports": {
|
||||
".": "./dist/index.js",
|
||||
"./net": "./dist/net.js",
|
||||
"./fs": "./dist/fs.js",
|
||||
"./env": "./dist/env.js",
|
||||
"./types": "./dist/types.js",
|
||||
"./time": "./dist/time.js",
|
||||
"./fs": "./dist/fs.js",
|
||||
"./html": "./dist/html.js",
|
||||
"./net": "./dist/net.js",
|
||||
"./time": "./dist/time.js",
|
||||
"./types": "./dist/types.js",
|
||||
"./url": "./dist/url.js",
|
||||
"./Logger": "./dist/Logger.js"
|
||||
},
|
||||
"publishConfig": {
|
||||
|
31
packages/utils/url.test.ts
Normal file
31
packages/utils/url.test.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import { fileUriToPath } from './url';
|
||||
|
||||
describe('utils/url', () => {
|
||||
|
||||
it('should convert a file URI to a file path', (async () => {
|
||||
// Tests imported from https://github.com/TooTallNate/file-uri-to-path/tree/master/test
|
||||
const testCases = {
|
||||
'file://host/path': '//host/path',
|
||||
'file://localhost/etc/fstab': '/etc/fstab',
|
||||
'file:///etc/fstab': '/etc/fstab',
|
||||
'file:///c:/WINDOWS/clock.avi': 'c:/WINDOWS/clock.avi',
|
||||
'file://localhost/c|/WINDOWS/clock.avi': 'c:/WINDOWS/clock.avi',
|
||||
'file:///c|/WINDOWS/clock.avi': 'c:/WINDOWS/clock.avi',
|
||||
'file://localhost/c:/WINDOWS/clock.avi': 'c:/WINDOWS/clock.avi',
|
||||
'file://hostname/path/to/the%20file.txt': '//hostname/path/to/the file.txt',
|
||||
'file:///c:/path/to/the%20file.txt': 'c:/path/to/the file.txt',
|
||||
'file:///C:/Documents%20and%20Settings/davris/FileSchemeURIs.doc': 'C:/Documents and Settings/davris/FileSchemeURIs.doc',
|
||||
'file:///C:/caf%C3%A9/%C3%A5r/d%C3%BCnn/%E7%89%9B%E9%93%83/Ph%E1%BB%9F/%F0%9F%98%B5.exe': 'C:/café/år/dünn/牛铃/Phở/😵.exe',
|
||||
};
|
||||
|
||||
for (const [input, expected] of Object.entries(testCases)) {
|
||||
const actual = fileUriToPath(input);
|
||||
expect(actual).toBe(expected);
|
||||
}
|
||||
|
||||
expect(fileUriToPath('file://c:/not/quite/right')).toBe('c:/not/quite/right');
|
||||
expect(fileUriToPath('file:///d:/better')).toBe('d:/better');
|
||||
expect(fileUriToPath('file:///c:/AUTOEXEC.BAT', 'win32')).toBe('c:\\AUTOEXEC.BAT');
|
||||
}));
|
||||
|
||||
});
|
98
packages/utils/url.ts
Normal file
98
packages/utils/url.ts
Normal file
@ -0,0 +1,98 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
|
||||
// This is a modified version of the file-uri-to-path package:
|
||||
//
|
||||
// - It removes the dependency to the "path" package, which wouldn't work with
|
||||
// React Native.
|
||||
//
|
||||
// - It always returns paths with forward slashes "/". This is normally handled
|
||||
// properly everywhere.
|
||||
//
|
||||
// - Adds the "platform" parameter to optionall return paths with "\" for win32
|
||||
function fileUriToPath_(uri: string, platform: string) {
|
||||
const sep = '/';
|
||||
|
||||
if (
|
||||
typeof uri !== 'string' ||
|
||||
uri.length <= 7 ||
|
||||
uri.substring(0, 7) !== 'file://'
|
||||
) {
|
||||
throw new TypeError(
|
||||
'must pass in a file:// URI to convert to a file path',
|
||||
);
|
||||
}
|
||||
|
||||
const rest = decodeURI(uri.substring(7));
|
||||
const firstSlash = rest.indexOf('/');
|
||||
let host = rest.substring(0, firstSlash);
|
||||
let path = rest.substring(firstSlash + 1);
|
||||
|
||||
// 2. Scheme Definition
|
||||
// As a special case, <host> can be the string "localhost" or the empty
|
||||
// string; this is interpreted as "the machine from which the URL is
|
||||
// being interpreted".
|
||||
if (host === 'localhost') {
|
||||
host = '';
|
||||
}
|
||||
|
||||
if (host) {
|
||||
host = sep + sep + host;
|
||||
}
|
||||
|
||||
// 3.2 Drives, drive letters, mount points, file system root
|
||||
// Drive letters are mapped into the top of a file URI in various ways,
|
||||
// depending on the implementation; some applications substitute
|
||||
// vertical bar ("|") for the colon after the drive letter, yielding
|
||||
// "file:///c|/tmp/test.txt". In some cases, the colon is left
|
||||
// unchanged, as in "file:///c:/tmp/test.txt". In other cases, the
|
||||
// colon is simply omitted, as in "file:///c/tmp/test.txt".
|
||||
path = path.replace(/^(.+)\|/, '$1:');
|
||||
|
||||
// for Windows, we need to invert the path separators from what a URI uses
|
||||
// if (sep === '\\') {
|
||||
// path = path.replace(/\//g, '\\');
|
||||
// }
|
||||
|
||||
if (/^.+:/.test(path)) {
|
||||
// has Windows drive at beginning of path
|
||||
} else {
|
||||
// unix path…
|
||||
path = sep + path;
|
||||
}
|
||||
|
||||
if (platform === 'win32') {
|
||||
return (host + path).replace(/\//g, '\\');
|
||||
} else {
|
||||
return host + path;
|
||||
}
|
||||
}
|
||||
|
||||
export const fileUriToPath = (path: string, platform = 'linux') => {
|
||||
const output = fileUriToPath_(path, platform);
|
||||
|
||||
// The file-uri-to-path module converts Windows path such as
|
||||
//
|
||||
// file://c:/autoexec.bat => \\c:\autoexec.bat
|
||||
//
|
||||
// Probably because a file:// that starts with only two slashes is not
|
||||
// quite valid. If we use three slashes, it works:
|
||||
//
|
||||
// file:///c:/autoexec.bat => c:\autoexec.bat
|
||||
//
|
||||
// However there are various places in the app where we can find
|
||||
// paths with only two slashes because paths are often constructed
|
||||
// as `file://${resourcePath}` - which works in all OSes except
|
||||
// Windows.
|
||||
//
|
||||
// So here we introduce a special case - if we detect that we have
|
||||
// an invalid Windows path that starts with \\x:, we just remove
|
||||
// the first two backslashes.
|
||||
//
|
||||
// https://github.com/laurent22/joplin/issues/5693
|
||||
|
||||
if (output.match(/^\/\/[a-zA-Z]:/)) {
|
||||
return output.substr(2);
|
||||
}
|
||||
|
||||
return output;
|
||||
};
|
@ -1,5 +1,14 @@
|
||||
# Joplin terminal app changelog
|
||||
|
||||
## [cli-v2.12.1](https://github.com/laurent22/joplin/releases/tag/cli-v2.12.1) - 2023-08-23T12:53:19Z
|
||||
|
||||
- New: Add support for share permissions (#8491)
|
||||
- Improved: Allow importing Evernote task lists (#8440 by Rob Moffat)
|
||||
- Improved: Rotating log files (#8376) (#5521 by [@hubert](https://github.com/hubert))
|
||||
- Improved: Updated packages @rmp135/sql-ts (v1.18.0), buildTools, clean-html (v2), dayjs (v1.11.9), domhandler (v5), gettext-parser (v7.0.1), glob (v10.3.3), highlight.js (v11.8.0), jsdom (v22.1.0), sass (v1.63.6), sharp (v0.32.3), standard (v17.1.0), word-wrap (v1.2.4)
|
||||
- Improved: WebDAV: Show a more descriptive error message when the password is empty (#8477) (#8466 by Henry Heino)
|
||||
- Security: Prevent XSS when passing specially encoded string to a link (57b4198)
|
||||
|
||||
## [cli-v2.11.1](https://github.com/laurent22/joplin/releases/tag/cli-v2.11.1) - 2023-06-27T09:28:01Z
|
||||
|
||||
- Improved: Updated packages aws, buildTools, domutils (v3.1.0), fs-extra (v11.1.1), jsdom (v21.1.2), markdown-it-multimd-table (v4.2.2), nanoid (v3.3.6), node-persist (v3.1.3), open (v8.4.2), reselect (v4.1.8), sass (v1.62.1), sharp (v0.32.1), sqlite3 (v5.1.6), tar (v6.1.15), turndown (v7.1.2), yargs (v17.7.2)
|
||||
|
57
yarn.lock
57
yarn.lock
@ -4626,7 +4626,7 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@joplin/fork-htmlparser2@^4.1.44, @joplin/fork-htmlparser2@^4.1.45, @joplin/fork-htmlparser2@workspace:packages/fork-htmlparser2":
|
||||
"@joplin/fork-htmlparser2@^4.1.46, @joplin/fork-htmlparser2@workspace:packages/fork-htmlparser2":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@joplin/fork-htmlparser2@workspace:packages/fork-htmlparser2"
|
||||
dependencies:
|
||||
@ -4647,7 +4647,7 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@joplin/fork-sax@^1.2.48, @joplin/fork-sax@workspace:packages/fork-sax":
|
||||
"@joplin/fork-sax@^1.2.50, @joplin/fork-sax@workspace:packages/fork-sax":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@joplin/fork-sax@workspace:packages/fork-sax"
|
||||
dependencies:
|
||||
@ -4656,7 +4656,7 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@joplin/fork-uslug@^1.0.9, @joplin/fork-uslug@workspace:packages/fork-uslug":
|
||||
"@joplin/fork-uslug@^1.0.11, @joplin/fork-uslug@workspace:packages/fork-uslug":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@joplin/fork-uslug@workspace:packages/fork-uslug"
|
||||
dependencies:
|
||||
@ -4666,11 +4666,11 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@joplin/htmlpack@workspace:packages/htmlpack, @joplin/htmlpack@~2.12":
|
||||
"@joplin/htmlpack@^2.12.1, @joplin/htmlpack@workspace:packages/htmlpack":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@joplin/htmlpack@workspace:packages/htmlpack"
|
||||
dependencies:
|
||||
"@joplin/fork-htmlparser2": ^4.1.45
|
||||
"@joplin/fork-htmlparser2": ^4.1.46
|
||||
"@types/fs-extra": 11.0.1
|
||||
css: 3.0.0
|
||||
datauri: 4.1.0
|
||||
@ -4679,20 +4679,20 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@joplin/lib@workspace:packages/lib, @joplin/lib@~2.12":
|
||||
"@joplin/lib@^2.12.1, @joplin/lib@workspace:packages/lib, @joplin/lib@~2.12":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@joplin/lib@workspace:packages/lib"
|
||||
dependencies:
|
||||
"@aws-sdk/client-s3": 3.296.0
|
||||
"@aws-sdk/s3-request-presigner": 3.296.0
|
||||
"@joplin/fork-htmlparser2": ^4.1.44
|
||||
"@joplin/fork-sax": ^1.2.48
|
||||
"@joplin/fork-uslug": ^1.0.9
|
||||
"@joplin/htmlpack": ~2.12
|
||||
"@joplin/renderer": ~2.12
|
||||
"@joplin/turndown": ^4.0.66
|
||||
"@joplin/turndown-plugin-gfm": ^1.0.48
|
||||
"@joplin/utils": ~2.12
|
||||
"@joplin/fork-htmlparser2": ^4.1.46
|
||||
"@joplin/fork-sax": ^1.2.50
|
||||
"@joplin/fork-uslug": ^1.0.11
|
||||
"@joplin/htmlpack": ^2.12.1
|
||||
"@joplin/renderer": ^2.12.1
|
||||
"@joplin/turndown": ^4.0.68
|
||||
"@joplin/turndown-plugin-gfm": ^1.0.50
|
||||
"@joplin/utils": ^2.12.1
|
||||
"@types/fs-extra": 11.0.1
|
||||
"@types/jest": 29.5.3
|
||||
"@types/js-yaml": 4.0.5
|
||||
@ -4796,9 +4796,9 @@ __metadata:
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@joplin/plugin-repo-cli@workspace:packages/plugin-repo-cli"
|
||||
dependencies:
|
||||
"@joplin/lib": ~2.12
|
||||
"@joplin/tools": ~2.12
|
||||
"@joplin/utils": ~2.12
|
||||
"@joplin/lib": ^2.12.1
|
||||
"@joplin/tools": ^2.12.1
|
||||
"@joplin/utils": ^2.12.1
|
||||
"@types/fs-extra": 11.0.1
|
||||
"@types/jest": 29.5.3
|
||||
"@types/node": 18.16.18
|
||||
@ -4847,12 +4847,13 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@joplin/renderer@workspace:packages/renderer, @joplin/renderer@~2.12":
|
||||
"@joplin/renderer@^2.12.1, @joplin/renderer@workspace:packages/renderer, @joplin/renderer@~2.12":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@joplin/renderer@workspace:packages/renderer"
|
||||
dependencies:
|
||||
"@joplin/fork-htmlparser2": ^4.1.44
|
||||
"@joplin/fork-uslug": ^1.0.9
|
||||
"@joplin/fork-htmlparser2": ^4.1.46
|
||||
"@joplin/fork-uslug": ^1.0.11
|
||||
"@joplin/utils": ~2.12
|
||||
"@types/jest": 29.5.3
|
||||
"@types/node": 18.16.18
|
||||
font-awesome-filetypes: 2.1.0
|
||||
@ -4945,14 +4946,14 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@joplin/tools@workspace:packages/tools, @joplin/tools@~2.12":
|
||||
"@joplin/tools@^2.12.1, @joplin/tools@workspace:packages/tools, @joplin/tools@~2.12":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@joplin/tools@workspace:packages/tools"
|
||||
dependencies:
|
||||
"@joplin/fork-htmlparser2": ^4.1.44
|
||||
"@joplin/lib": ~2.12
|
||||
"@joplin/renderer": ~2.12
|
||||
"@joplin/utils": ~2.12
|
||||
"@joplin/fork-htmlparser2": ^4.1.46
|
||||
"@joplin/lib": ^2.12.1
|
||||
"@joplin/renderer": ^2.12.1
|
||||
"@joplin/utils": ^2.12.1
|
||||
"@rmp135/sql-ts": 1.18.0
|
||||
"@types/fs-extra": 11.0.1
|
||||
"@types/jest": 29.5.3
|
||||
@ -4988,7 +4989,7 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@joplin/turndown-plugin-gfm@^1.0.48, @joplin/turndown-plugin-gfm@workspace:packages/turndown-plugin-gfm":
|
||||
"@joplin/turndown-plugin-gfm@^1.0.50, @joplin/turndown-plugin-gfm@workspace:packages/turndown-plugin-gfm":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@joplin/turndown-plugin-gfm@workspace:packages/turndown-plugin-gfm"
|
||||
dependencies:
|
||||
@ -5000,7 +5001,7 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@joplin/turndown@^4.0.66, @joplin/turndown@workspace:packages/turndown":
|
||||
"@joplin/turndown@^4.0.68, @joplin/turndown@workspace:packages/turndown":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@joplin/turndown@workspace:packages/turndown"
|
||||
dependencies:
|
||||
@ -5017,7 +5018,7 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@joplin/utils@workspace:packages/utils, @joplin/utils@~2.12":
|
||||
"@joplin/utils@^2.12.1, @joplin/utils@workspace:packages/utils, @joplin/utils@~2.12":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@joplin/utils@workspace:packages/utils"
|
||||
dependencies:
|
||||
|
Loading…
Reference in New Issue
Block a user