1
0
mirror of https://github.com/laurent22/joplin.git synced 2026-04-05 11:01:11 +02:00

Compare commits

..

1 Commits

Author SHA1 Message Date
Laurent Cozic
683dea01ba init 2025-03-03 20:20:05 +00:00
17 changed files with 144 additions and 58 deletions

View File

@@ -29,6 +29,7 @@ const EncryptionService = require('@joplin/lib/services/e2ee/EncryptionService')
const FileApiDriverLocal = require('@joplin/lib/file-api-driver-local').default;
const React = require('react');
const nodeSqlite = require('sqlite3');
const nodeSqliteCipher = require('@journeyapps/sqlcipher');
const initLib = require('@joplin/lib/initLib').default;
const pdfJs = require('pdfjs-dist');
require('@sentry/electron/renderer');
@@ -109,6 +110,7 @@ const main = async () => {
appVersion,
electronBridge: bridge(),
nodeSqlite,
nodeSqliteCipher,
pdfJs,
});

View File

@@ -166,6 +166,7 @@
"@joplin/lib": "~3.3",
"@joplin/renderer": "~3.3",
"@joplin/utils": "~3.3",
"@journeyapps/sqlcipher": "5.3.1",
"@sentry/electron": "4.24.0",
"@types/mustache": "4.2.5",
"async-mutex": "0.5.0",

View File

@@ -86,8 +86,8 @@ android {
applicationId "net.cozic.joplin"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 2097765
versionName "3.3.2"
versionCode 2097764
versionName "3.3.1"
ndk {
abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
}

View File

@@ -14,7 +14,7 @@ import { createStore, applyMiddleware, Store } from 'redux';
import { defaultState, stateUtils } from './reducer';
import JoplinDatabase from './JoplinDatabase';
import { cancelTimers as folderScreenUtilsCancelTimers, refreshFolders, scheduleRefreshFolders } from './folders-screen-utils';
const { DatabaseDriverNode } = require('./database-driver-node.js');
const { DatabaseDriverNode } = require('./database-driver-node-sqlcipher.js');
import BaseModel from './BaseModel';
import Folder from './models/Folder';
import BaseItem from './models/BaseItem';

View File

@@ -0,0 +1,90 @@
const shim = require('./shim').default;
const Promise = require('promise');
class DatabaseDriverNode {
async open(options) {
await this.open_(options);
await this.exec('PRAGMA key = \'mysecret\'');
}
open_(options) {
return new Promise((resolve, reject) => {
const sqlite3 = shim.nodeSqliteCipher().verbose();
this.db_ = new sqlite3.Database(options.name, sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, error => {
if (error) {
reject(error);
return;
}
resolve();
});
});
}
sqliteErrorToJsError(error, sql = null, params = null) {
const msg = [error.toString()];
if (sql) msg.push(sql);
if (params) msg.push(params);
const output = new Error(msg.join(': '));
if (error.code) output.code = error.code;
return output;
}
selectOne(sql, params = null) {
if (!params) params = {};
return new Promise((resolve, reject) => {
this.db_.get(sql, params, (error, row) => {
if (error) {
reject(error);
return;
}
resolve(row);
});
});
}
loadExtension(path) {
return new Promise((resolve, reject) => {
this.db_.loadExtension(path, (error) => {
if (error) {
reject(error);
} else {
resolve();
}
});
});
}
selectAll(sql, params = null) {
if (!params) params = {};
return new Promise((resolve, reject) => {
this.db_.all(sql, params, (error, row) => {
if (error) {
reject(error);
return;
}
resolve(row);
});
});
}
exec(sql, params = null) {
if (!params) params = {};
return new Promise((resolve, reject) => {
this.db_.run(sql, params, error => {
if (error) {
reject(error);
return;
}
resolve();
});
});
}
lastInsertId() {
throw new Error('NOT IMPLEMENTED');
}
}
module.exports = { DatabaseDriverNode };

View File

@@ -2,6 +2,7 @@ const { afterEachCleanUp } = require('./testing/test-utils.js');
const { shimInit } = require('./shim-init-node.js');
const sharp = require('sharp');
const nodeSqlite = require('sqlite3');
const nodeSqliteCipher = require('@journeyapps/sqlcipher');
const pdfJs = require('pdfjs-dist');
const packageInfo = require('./package.json');
@@ -10,7 +11,7 @@ const React = require('react');
require('../../jest.base-setup.js')();
shimInit({ sharp, nodeSqlite, pdfJs, React, appVersion: () => packageInfo.version });
shimInit({ sharp, nodeSqlite, nodeSqliteCipher, pdfJs, React, appVersion: () => packageInfo.version });
global.afterEach(async () => {
await afterEachCleanUp();

View File

@@ -52,6 +52,7 @@
"@joplin/turndown": "^4.0.79",
"@joplin/turndown-plugin-gfm": "^1.0.61",
"@joplin/utils": "~3.3",
"@journeyapps/sqlcipher": "5.3.1",
"@types/nanoid": "3.0.0",
"adm-zip": "0.5.16",
"async-mutex": "0.5.0",

View File

@@ -110,6 +110,8 @@ interface ShimInitOptions {
electronBridge: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
nodeSqlite: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
nodeSqliteCipher: any;
pdfJs: typeof pdfJsNamespace;
}
@@ -122,6 +124,7 @@ function shimInit(options: ShimInitOptions = null) {
electronBridge: null,
nodeSqlite: null,
pdfJs: null,
nodeSqliteCipher: null,
...options,
};
@@ -131,6 +134,7 @@ function shimInit(options: ShimInitOptions = null) {
const pdfJs = options.pdfJs;
shim.setNodeSqlite(options.nodeSqlite);
shim.setNodeSqliteCipher(options.nodeSqliteCipher);
shim.fsDriver = () => {
throw new Error('Not implemented');

View File

@@ -76,6 +76,8 @@ let isTestingEnv_ = false;
let react_: typeof React = null;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
let nodeSqlite_: any = null;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
let nodeSqliteCipher_: any = null;
const shim = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
@@ -480,11 +482,21 @@ const shim = {
nodeSqlite_ = nodeSqlite;
},
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
setNodeSqliteCipher: (nodeSqlite: any) => {
nodeSqliteCipher_ = nodeSqlite;
},
nodeSqlite: () => {
if (!nodeSqlite_) throw new Error('Trying to access nodeSqlite before it has been set!!!');
return nodeSqlite_;
},
nodeSqliteCipher: () => {
if (!nodeSqliteCipher_) throw new Error('Trying to access nodeSqliteCipher before it has been set!!!');
return nodeSqliteCipher_;
},
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
setReact: (react: any) => {
react_ = react;

View File

@@ -18,7 +18,7 @@ import OneDriveApi from '../onedrive-api';
import SyncTargetOneDrive from '../SyncTargetOneDrive';
import JoplinDatabase from '../JoplinDatabase';
import * as fs from 'fs-extra';
const { DatabaseDriverNode } = require('../database-driver-node.js');
const { DatabaseDriverNode } = require('../database-driver-node-sqlcipher.js');
import Folder from '../models/Folder';
import Note from '../models/Note';
import ItemChange from '../models/ItemChange';

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/server",
"version": "3.3.4",
"version": "3.3.3",
"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,6 +1,5 @@
import { basename } from '@joplin/utils/path';
import { Item } from '../services/database/types';
import { itemIsEncrypted, localFileFromUrl } from './joplinUtils';
import { itemIsEncrypted } from './joplinUtils';
import { expectThrow } from './testing/testUtils';
describe('joplinUtils', () => {
@@ -22,21 +21,4 @@ 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,7 +26,6 @@ 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');
@@ -117,26 +116,11 @@ export function isJoplinResourceBlobPath(path: string): boolean {
return path.indexOf(resourceDirName) === 0;
}
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> {
export async function localFileFromUrl(url: string): Promise<string> {
const cssPluginAssets = 'css/pluginAssets/';
const jsPluginAssets = 'js/pluginAssets/';
const baseUrls = [cssPluginAssets, jsPluginAssets];
for (const baseUrl of baseUrls) {
if (urlPath.startsWith(baseUrl)) {
const pluginAssetPath = urlPath.substring(baseUrl.length);
return resolveUnsafeAssetPath(pluginAssetPath);
}
}
if (url.indexOf(cssPluginAssets) === 0) return `${pluginAssetRootDir_}/${url.substr(cssPluginAssets.length)}`;
if (url.indexOf(jsPluginAssets) === 0) return `${pluginAssetRootDir_}/${url.substr(jsPluginAssets.length)}`;
return null;
}

View File

@@ -172,4 +172,5 @@ sideloading
ggml
Minidump
collapseall
newfolder
newfolder
sqlcipher

View File

@@ -1,14 +1,5 @@
# Joplin Android Changelog
## [android-v3.3.2](https://github.com/laurent22/joplin/releases/tag/android-v3.3.2) (Pre-release) - 2025-03-03T22:35:08Z
- Improved: Improve encryption config screen accessibility (#11874) (#11846 by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Improved: Switch default library used for Whisper voice typing (#11881 by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Improved: Updated packages @bam.tech/react-native-image-resizer (v3.0.11)
- Fixed: Accessibility: Fix "new note" and "new to-do" buttons are focusable even while invisible (#11899 by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
- Fixed: Fix disabled encryption keys list showing enabled keys (#11861) (#11858 by [@pedr](https://github.com/pedr))
- Fixed: Fix voice recorder crash (#11876) (#11864 by [@personalizedrefrigerator](https://github.com/personalizedrefrigerator))
## [android-v3.3.1](https://github.com/laurent22/joplin/releases/tag/android-v3.3.1) (Pre-release) - 2025-02-19T16:01:54Z
- New: Add support for plugin editor views (#11831)

View File

@@ -1,9 +1,5 @@
# 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)

View File

@@ -8279,6 +8279,7 @@ __metadata:
"@joplin/renderer": ~3.3
"@joplin/tools": ~3.3
"@joplin/utils": ~3.3
"@journeyapps/sqlcipher": 5.3.1
"@playwright/test": 1.45.3
"@sentry/electron": 4.24.0
"@testing-library/react-hooks": 8.0.1
@@ -8604,6 +8605,7 @@ __metadata:
"@joplin/turndown": ^4.0.79
"@joplin/turndown-plugin-gfm": ^1.0.61
"@joplin/utils": ~3.3
"@journeyapps/sqlcipher": 5.3.1
"@testing-library/react-hooks": 8.0.1
"@types/adm-zip": 0.5.7
"@types/fs-extra": 11.0.4
@@ -8998,6 +9000,16 @@ __metadata:
languageName: unknown
linkType: soft
"@journeyapps/sqlcipher@npm:5.3.1":
version: 5.3.1
resolution: "@journeyapps/sqlcipher@npm:5.3.1"
dependencies:
"@mapbox/node-pre-gyp": ^1.0.0
node-addon-api: ^3.0.0
checksum: 3e3dfeb85dffd1bab20f41cb6b38df1dc57baa33266293e55ad883d7d181294e45e8496cd888457689fa9e440d5629712c72124e05ae9ce00fe5175129e2c9db
languageName: node
linkType: hard
"@jridgewell/gen-mapping@npm:^0.3.0":
version: 0.3.3
resolution: "@jridgewell/gen-mapping@npm:0.3.3"
@@ -35378,6 +35390,15 @@ __metadata:
languageName: node
linkType: hard
"node-addon-api@npm:^3.0.0":
version: 3.2.1
resolution: "node-addon-api@npm:3.2.1"
dependencies:
node-gyp: latest
checksum: 2369986bb0881ccd9ef6bacdf39550e07e089a9c8ede1cbc5fc7712d8e2faa4d50da0e487e333d4125f8c7a616c730131d1091676c9d499af1d74560756b4a18
languageName: node
linkType: hard
"node-addon-api@npm:^4.2.0, node-addon-api@npm:^4.3.0":
version: 4.3.0
resolution: "node-addon-api@npm:4.3.0"