You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2026-04-05 11:01:11 +02:00
Compare commits
1 Commits
android-v3
...
sqlcipher_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
683dea01ba |
@@ -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,
|
||||
});
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
90
packages/lib/database-driver-node-sqlcipher.js
Normal file
90
packages/lib/database-driver-node-sqlcipher.js
Normal 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 };
|
||||
@@ -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();
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -172,4 +172,5 @@ sideloading
|
||||
ggml
|
||||
Minidump
|
||||
collapseall
|
||||
newfolder
|
||||
newfolder
|
||||
sqlcipher
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
21
yarn.lock
21
yarn.lock
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user