A few improvements, requested from the review:

- File reading is now happening later in the process;
- File reading now reuses methods from `core/attachment`;
- Errors thrown in the import now include an error message;
- Adds prettier command to "prettify" all files;
- Adds prettier command to check for files not conforming to prettier's config;
- Makes CI run the prettier:check command;
- Changes `export const method = () => {};` to `export function method() {}` in the `1pux-parser` file;
- Other minor consistency changes/fixes/improvements;
- Actually ran prettier on every file, and it had a lot of things to tweak.
This commit is contained in:
Bruno Bernardino 2021-12-20 15:01:25 +00:00
parent db18adf7fd
commit 50ae105b45
No known key found for this signature in database
GPG Key ID: D1B0A69ADD114ECE
67 changed files with 20160 additions and 26497 deletions

View File

@ -2,43 +2,43 @@ name: "Build And Publish Desktop Tauri Apps"
on: [workflow_dispatch] on: [workflow_dispatch]
jobs: jobs:
build_and_publish: build_and_publish:
name: Build And Publish name: Build And Publish
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
platform: [macos-latest, ubuntu-latest, windows-latest] platform: [macos-latest, ubuntu-latest, windows-latest]
runs-on: ${{ matrix.platform }} runs-on: ${{ matrix.platform }}
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: setup node - name: setup node
uses: actions/setup-node@v1 uses: actions/setup-node@v1
with: with:
node-version: 12 node-version: 12
- name: install Rust stable - name: install Rust stable
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
with: with:
toolchain: stable toolchain: stable
- name: install tauri bundler - name: install tauri bundler
run: cargo install tauri-bundler --force run: cargo install tauri-bundler --force
- name: install webkit2gtk (ubuntu only) - name: install webkit2gtk (ubuntu only)
if: matrix.platform == 'ubuntu-latest' if: matrix.platform == 'ubuntu-latest'
run: | run: |
sudo apt-get update sudo apt-get update
sudo apt-get install -y webkit2gtk-4.0 sudo apt-get install -y webkit2gtk-4.0
- name: install app dependencies and bootstrap packages - name: install app dependencies and bootstrap packages
run: npm install run: npm install
- uses: tauri-apps/tauri-action@v0 - uses: tauri-apps/tauri-action@v0
env: env:
PL_SERVER_URL: https://api.padloc.app PL_SERVER_URL: https://api.padloc.app
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: with:
tagName: tauri-v__VERSION__ # the action automatically replaces \_\_VERSION\_\_ with the app version tagName: tauri-v__VERSION__ # the action automatically replaces \_\_VERSION\_\_ with the app version
releaseName: "Padloc (Tauri Edition) v__VERSION__" releaseName: "Padloc (Tauri Edition) v__VERSION__"
body: "WARNING: The builds in this release are experimental. Use at your own risk!" body: "WARNING: The builds in this release are experimental. Use at your own risk!"
draft: true draft: true
prerelease: true prerelease: true
projectPath: packages/tauri projectPath: packages/tauri
npmScript: build npmScript: build

View File

@ -3,18 +3,20 @@ name: Run Tests
on: [push] on: [push]
jobs: jobs:
test: test:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: actions/setup-node@v1 - uses: actions/setup-node@v1
with: with:
node-version: 16.13.1 node-version: 16.13.1
- name: Install dependencies - name: Install dependencies
run: npm ci run: npm ci
- name: Run pwa test build - name: Run prettier check
run: npm run pwa:build run: npm run prettier:check
- name: Test starting zero-config server - name: Run pwa test build
run: npm run server:start-dry run: npm run pwa:build
#- name: Run tests - name: Test starting zero-config server
# run: npm test run: npm run server:start-dry
#- name: Run tests
# run: npm test

View File

@ -1 +1,2 @@
app/src/core/*.js app/src/core/*.js
package-lock.json

View File

@ -1,8 +1,6 @@
{ {
"lit-plugin.globalAttributes": [ "lit-plugin.globalAttributes": ["disabled"],
"disabled"
],
"files.associations": { "files.associations": {
"*.svg": "html" "*.svg": "html"
} }
} }

View File

@ -63,6 +63,8 @@ For more configuration options, see [Configuration](#configuration)
To add dependencies, you can use `scope=[scope-without-@padloc/] npm run add [package]` and to remove them, run `scope=[scope-without-@padloc/] npm run remove [package]`. To add dependencies, you can use `scope=[scope-without-@padloc/] npm run add [package]` and to remove them, run `scope=[scope-without-@padloc/] npm run remove [package]`.
Use `npm run prettier` to make "prettify" all files.
## Configuration ## Configuration
| Environment Variable | Default | Description | | Environment Variable | Default | Description |

View File

@ -7,4 +7,4 @@
"appId": "app.padloc", "appId": "app.padloc",
"scheme": "padloc", "scheme": "padloc",
"author": "Martin Kleinschrodt <martin@maklesoft.com>" "author": "Martin Kleinschrodt <martin@maklesoft.com>"
} }

View File

@ -1,4 +1,4 @@
module.exports = function(config) { module.exports = function (config) {
config.set({ config.set({
frameworks: ["mocha", "chai"], frameworks: ["mocha", "chai"],
files: ["test/tests.js"], files: ["test/tests.js"],
@ -9,6 +9,6 @@ module.exports = function(config) {
browsers: ["ChromeHeadless"], browsers: ["ChromeHeadless"],
autoWatch: false, autoWatch: false,
// singleRun: false, // Karma captures browsers, runs the tests and exits // singleRun: false, // Karma captures browsers, runs the tests and exits
concurrency: Infinity concurrency: Infinity,
}); });
}; };

View File

@ -1,7 +1,5 @@
{ {
"packages": [ "packages": ["packages/*"],
"packages/*" "version": "4.0.0",
], "exact": true
"version": "4.0.0",
"exact": true
} }

19
package-lock.json generated
View File

@ -11,6 +11,7 @@
"license": "GPL-3.0", "license": "GPL-3.0",
"devDependencies": { "devDependencies": {
"lerna": "4.0.0", "lerna": "4.0.0",
"prettier": "2.5.1",
"ts-node": "10.0.0", "ts-node": "10.0.0",
"typescript": "4.4.3" "typescript": "4.4.3"
}, },
@ -5658,6 +5659,18 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/prettier": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz",
"integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==",
"dev": true,
"bin": {
"prettier": "bin-prettier.js"
},
"engines": {
"node": ">=10.13.0"
}
},
"node_modules/process-nextick-args": { "node_modules/process-nextick-args": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
@ -12266,6 +12279,12 @@
"find-up": "^4.0.0" "find-up": "^4.0.0"
} }
}, },
"prettier": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz",
"integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==",
"dev": true
},
"process-nextick-args": { "process-nextick-args": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",

View File

@ -1,40 +1,43 @@
{ {
"name": "padloc", "name": "padloc",
"private": true, "private": true,
"version": "4.0.0", "version": "4.0.0",
"description": "A minimalist password manager", "description": "A minimalist password manager",
"author": "Martin Kleinschrodt <martin@maklesoft.com>", "author": "Martin Kleinschrodt <martin@maklesoft.com>",
"license": "GPL-3.0", "license": "GPL-3.0",
"homepage": "https://padlock.io/", "homepage": "https://padloc.app/",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/maklesoft/padlock.git" "url": "git+https://github.com/padloc/padloc.git"
}, },
"engines": { "engines": {
"node": "16.13.1", "node": "16.13.1",
"npm": "8.2.0" "npm": "8.2.0"
}, },
"main": "main.js", "main": "main.js",
"devDependencies": { "devDependencies": {
"lerna": "4.0.0", "lerna": "4.0.0",
"ts-node": "10.0.0", "prettier": "2.5.1",
"typescript": "4.4.3" "ts-node": "10.0.0",
}, "typescript": "4.4.3"
"scripts": { },
"postinstall": "lerna bootstrap", "scripts": {
"bootstrap": "lerna bootstrap", "postinstall": "lerna bootstrap",
"pwa:build": "lerna run build --scope @padloc/pwa", "bootstrap": "lerna bootstrap",
"pwa:start": "lerna run start --scope @padloc/pwa", "pwa:build": "lerna run build --scope @padloc/pwa",
"server:start": "lerna run start --scope @padloc/server --stream", "pwa:start": "lerna run start --scope @padloc/pwa",
"server:start-dry": "lerna run start-dry --stream --scope @padloc/server", "server:start": "lerna run start --scope @padloc/server --stream",
"electron:build": "cd packages/electron && npm run build && cd ../..", "server:start-dry": "lerna run start-dry --stream --scope @padloc/server",
"start": "npm run pwa:build && lerna run --scope '@padloc/{server,pwa}' --parallel start", "electron:build": "cd packages/electron && npm run build && cd ../..",
"dev": "lerna run --parallel --scope '@padloc/{server,pwa}' --parallel dev", "start": "npm run pwa:build && lerna run --scope '@padloc/{server,pwa}' --parallel start",
"tauri:dev": "lerna run --parallel --scope '@padloc/{server,tauri}' --parallel dev", "dev": "lerna run --parallel --scope '@padloc/{server,pwa}' --parallel dev",
"repl": "cd packages/server && npm run repl && cd ../..", "tauri:dev": "lerna run --parallel --scope '@padloc/{server,tauri}' --parallel dev",
"test": "lerna run test", "repl": "cd packages/server && npm run repl && cd ../..",
"locale:extract": "lerna run extract --scope '@padloc/locale'", "test": "lerna run test",
"add": "lerna add $1 --scope=@padloc/$scope", "locale:extract": "lerna run extract --scope '@padloc/locale'",
"remove": "rm packages/$scope/package-lock.json && lerna exec \"npm uninstall $1\" --scope=@padloc/$scope" "add": "lerna add $1 --scope=@padloc/$scope",
} "remove": "rm packages/$scope/package-lock.json && lerna exec \"npm uninstall $1\" --scope=@padloc/$scope",
"prettier": "prettier --write .",
"prettier:check": "prettier --check ."
}
} }

View File

@ -1,64 +1,64 @@
{ {
"name": "@padloc/app", "name": "@padloc/app",
"version": "4.0.0", "version": "4.0.0",
"author": "Martin Kleinschrodt <martin@maklesoft.com>", "author": "Martin Kleinschrodt <martin@maklesoft.com>",
"license": "GPL-3.0", "license": "GPL-3.0",
"private": true, "private": true,
"files": [ "files": [
"src", "src",
"assets", "assets",
"types", "types",
"tsconfig.json" "tsconfig.json"
], ],
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/padloc/padloc.git", "url": "https://github.com/padloc/padloc.git",
"directory": "packages/app" "directory": "packages/app"
}, },
"engines": { "engines": {
"node": "16.13.1", "node": "16.13.1",
"npm": "8.2.0" "npm": "8.2.0"
}, },
"dependencies": { "dependencies": {
"@padloc/core": "4.0.0", "@padloc/core": "4.0.0",
"@padloc/locale": "4.0.0", "@padloc/locale": "4.0.0",
"@simplewebauthn/browser": "4.0.0", "@simplewebauthn/browser": "4.0.0",
"@simplewebauthn/typescript-types": "4.0.0", "@simplewebauthn/typescript-types": "4.0.0",
"@types/dompurify": "2.3.1", "@types/dompurify": "2.3.1",
"@types/marked": "3.0.1", "@types/marked": "3.0.1",
"@types/papaparse": "5.2.5", "@types/papaparse": "5.2.5",
"@types/qrcode": "1.4.1", "@types/qrcode": "1.4.1",
"@types/ua-parser-js": "0.7.36", "@types/ua-parser-js": "0.7.36",
"@types/workbox-precaching": "4.3.1", "@types/workbox-precaching": "4.3.1",
"@types/workbox-sw": "4.3.1", "@types/workbox-sw": "4.3.1",
"@types/workbox-window": "4.3.3", "@types/workbox-window": "4.3.3",
"@types/zxcvbn": "4.4.1", "@types/zxcvbn": "4.4.1",
"@webcomponents/webcomponentsjs": "2.5.0", "@webcomponents/webcomponentsjs": "2.5.0",
"autosize": "5.0.0", "autosize": "5.0.0",
"date-fns": "2.22.1", "date-fns": "2.22.1",
"dompurify": "2.3.3", "dompurify": "2.3.3",
"event-target-shim": "6.0.2", "event-target-shim": "6.0.2",
"http-server": "0.12.3", "http-server": "0.12.3",
"jsqr": "1.4.0", "jsqr": "1.4.0",
"jszip": "3.7.1", "jszip": "3.7.1",
"lit": "2.0.0-rc.2", "lit": "2.0.0-rc.2",
"localforage": "1.9.0", "localforage": "1.9.0",
"marked": "3.0.4", "marked": "3.0.4",
"papaparse": "5.3.1", "papaparse": "5.3.1",
"qrcode": "1.4.4", "qrcode": "1.4.4",
"reflect-metadata": "0.1.13", "reflect-metadata": "0.1.13",
"typescript": "4.4.3", "typescript": "4.4.3",
"ua-parser-js": "0.7.28", "ua-parser-js": "0.7.28",
"workbox-precaching": "6.2.0", "workbox-precaching": "6.2.0",
"workbox-sw": "6.1.5", "workbox-sw": "6.1.5",
"workbox-window": "6.1.5", "workbox-window": "6.1.5",
"zxcvbn": "4.4.2" "zxcvbn": "4.4.2"
}, },
"devDependencies": { "devDependencies": {
"@types/chai": "4.2.18", "@types/chai": "4.2.18",
"@types/mocha": "8.2.2", "@types/mocha": "8.2.2",
"chai": "4.3.4", "chai": "4.3.4",
"mocha": "8.4.0" "mocha": "8.4.0"
}, },
"description": "Padloc Web-Based UI package" "description": "Padloc Web-Based UI package"
} }

View File

@ -45,7 +45,7 @@ export class Dialog<I, R> extends LitElement {
Dialog.openDialogs.delete(this); Dialog.openDialogs.delete(this);
} }
async show(_input: I = (undefined as any) as I) { async show(_input: I = undefined as any as I) {
Dialog.openDialogs.add(this); Dialog.openDialogs.add(this);
this.open = true; this.open = true;

View File

@ -15,7 +15,7 @@ import { stringToBytes } from "@padloc/core/src/encoding";
@customElement("pl-import-dialog") @customElement("pl-import-dialog")
export class ImportDialog extends Dialog<File, void> { export class ImportDialog extends Dialog<File, void> {
@state() @state()
private _rawData: null | string | ArrayBuffer = ""; private _file: File;
@state() @state()
private _items: VaultItem[] = []; private _items: VaultItem[] = [];
@ -71,19 +71,10 @@ export class ImportDialog extends Dialog<File, void> {
await this.updateComplete; await this.updateComplete;
const result = super.show(); const result = super.show();
const reader = new FileReader(); this._file = file;
reader.onload = async () => { this._formatSelect.value = ((await imp.guessFormat(file)) || imp.CSV).value;
this._rawData = reader.result; await this._parseData();
this._formatSelect.value = (imp.guessFormat(file, this._rawData) || imp.CSV).value; this._vaultSelect.value = app.mainVault!;
this._parseData();
this._vaultSelect.value = app.mainVault!;
};
if (imp.doesFileRequireReadingAsBinary(file)) {
reader.readAsArrayBuffer(file);
} else {
reader.readAsText(file);
}
return result; return result;
} }
@ -100,7 +91,7 @@ Github,"work,coding",https://github.com,john.doe@gmail.com,129lskdf93`)
} }
private async _parseData(): Promise<void> { private async _parseData(): Promise<void> {
const rawStr = this._rawData; const file = this._file;
switch (this._formatSelect.value) { switch (this._formatSelect.value) {
case imp.PADLOCK_LEGACY.value: case imp.PADLOCK_LEGACY.value:
@ -111,7 +102,7 @@ Github,"work,coding",https://github.com,john.doe@gmail.com,129lskdf93`)
type: "password", type: "password",
validate: async (pwd: string) => { validate: async (pwd: string) => {
try { try {
this._items = await imp.asPadlockLegacy(rawStr as string, pwd); this._items = await imp.asPadlockLegacy(file, pwd);
} catch (e) { } catch (e) {
throw $l("Wrong Password"); throw $l("Wrong Password");
} }
@ -125,13 +116,13 @@ Github,"work,coding",https://github.com,john.doe@gmail.com,129lskdf93`)
} }
break; break;
case imp.LASTPASS.value: case imp.LASTPASS.value:
this._items = await imp.asLastPass(rawStr as string); this._items = await imp.asLastPass(file);
break; break;
case imp.CSV.value: case imp.CSV.value:
this._items = await imp.asCSV(rawStr as string); this._items = await imp.asCSV(file);
break; break;
case imp.ONEPUX.value: case imp.ONEPUX.value:
this._items = await imp.as1Pux(rawStr); this._items = await imp.as1Pux(file);
break; break;
case imp.PBES2.value: case imp.PBES2.value:
this.open = false; this.open = false;
@ -141,7 +132,7 @@ Github,"work,coding",https://github.com,john.doe@gmail.com,129lskdf93`)
type: "password", type: "password",
validate: async (pwd: string) => { validate: async (pwd: string) => {
try { try {
this._items = await imp.asPBES2Container(rawStr as string, pwd); this._items = await imp.asPBES2Container(file, pwd);
} catch (e) { } catch (e) {
throw $l("Wrong Password"); throw $l("Wrong Password");
} }

View File

@ -173,9 +173,8 @@ export class Popover extends LitElement {
} }
private _getAutoAlignment(): PopoverAlignment { private _getAutoAlignment(): PopoverAlignment {
const preferred = (Array.isArray(this.preferAlignment) const preferred = (
? [...this.preferAlignment] Array.isArray(this.preferAlignment) ? [...this.preferAlignment] : [this.preferAlignment]
: [this.preferAlignment]
).reverse(); ).reverse();
const alignments = [...ALIGNMENTS].sort((a, b) => preferred.indexOf(b) - preferred.indexOf(a)); const alignments = [...ALIGNMENTS].sort((a, b) => preferred.indexOf(b) - preferred.indexOf(a));
return alignments.find((alignment) => this._isWithinBounds(this._getPosition(alignment))) || alignments[0]; return alignments.find((alignment) => this._isWithinBounds(this._getPosition(alignment))) || alignments[0];

View File

@ -65,7 +65,12 @@ export class SettingsTools extends StateMixin(LitElement) {
</pl-scroller> </pl-scroller>
</div> </div>
<input type="file" accept="text/plain,.csv,.pls,.set,.pbes2,.1pux" hidden @change=${() => this._importFile()} /> <input
type="file"
accept="text/plain,.csv,.pls,.set,.pbes2,.1pux"
hidden
@change=${() => this._importFile()}
/>
`; `;
} }
} }

View File

@ -1,308 +1,283 @@
import { loadAsync } from 'jszip'; import { loadAsync } from "jszip";
export type OnePuxItemDetailsLoginField = { export type OnePuxItemDetailsLoginField = {
value: string; value: string;
id: string; id: string;
name: string; name: string;
fieldType: 'A' | 'B' | 'C' | 'E' | 'I' | 'N' | 'P' | 'R' | 'S' | 'T' | 'U'; fieldType: "A" | "B" | "C" | "E" | "I" | "N" | "P" | "R" | "S" | "T" | "U";
designation?: 'username' | 'password'; designation?: "username" | "password";
}; };
export type OnePuxItemDetailsSection = { export type OnePuxItemDetailsSection = {
title: string; title: string;
name: string; name: string;
fields: [ fields: [
{ {
title: string; title: string;
id: string; id: string;
value: { value: {
concealed?: string; concealed?: string;
reference?: string; reference?: string;
string?: string; string?: string;
email?: string; email?: string;
phone?: string; phone?: string;
url?: string; url?: string;
totp?: string; totp?: string;
gender?: string; gender?: string;
creditCardType?: string; creditCardType?: string;
creditCardNumber?: string; creditCardNumber?: string;
monthYear?: number; monthYear?: number;
date?: number; date?: number;
}; };
indexAtSource: number; indexAtSource: number;
guarded: boolean; guarded: boolean;
multiline: boolean; multiline: boolean;
dontGenerate: boolean; dontGenerate: boolean;
inputTraits: { inputTraits: {
keyboard: string; keyboard: string;
correction: string; correction: string;
capitalization: string; capitalization: string;
}; };
}, }
]; ];
}; };
export type OnePuxItemDetailsPasswordHistory = { export type OnePuxItemDetailsPasswordHistory = {
value: string; value: string;
time: number; time: number;
}; };
export type OnePuxItemOverviewUrl = { export type OnePuxItemOverviewUrl = {
label: string; label: string;
url: string; url: string;
}; };
export type OnePuxItem = { export type OnePuxItem = {
item?: { item?: {
uuid: string; uuid: string;
favIndex: number; favIndex: number;
createdAt: number; createdAt: number;
updatedAt: number; updatedAt: number;
trashed: boolean; trashed: boolean;
categoryUuid: string; categoryUuid: string;
details: { details: {
loginFields: OnePuxItemDetailsLoginField[]; loginFields: OnePuxItemDetailsLoginField[];
notesPlain?: string; notesPlain?: string;
sections: OnePuxItemDetailsSection[]; sections: OnePuxItemDetailsSection[];
passwordHistory: OnePuxItemDetailsPasswordHistory[]; passwordHistory: OnePuxItemDetailsPasswordHistory[];
documentAttributes?: { documentAttributes?: {
fileName: string; fileName: string;
documentId: string; documentId: string;
decryptedSize: number; decryptedSize: number;
}; };
};
overview: {
subtitle: string;
urls?: OnePuxItemOverviewUrl[];
title: string;
url: string;
ps?: number;
pbe?: number;
pgrng?: boolean;
tags?: string[];
};
}; };
overview: { file?: {
subtitle: string; attrs: {
urls?: OnePuxItemOverviewUrl[]; uuid: string;
title: string; name: string;
url: string; type: string;
ps?: number; };
pbe?: number; path: string;
pgrng?: boolean;
tags?: string[];
}; };
};
file?: {
attrs: {
uuid: string;
name: string;
type: string;
};
path: string;
};
}; };
export type OnePuxVault = { export type OnePuxVault = {
attrs: { attrs: {
uuid: string; uuid: string;
desc: string; desc: string;
avatar: string; avatar: string;
name: string; name: string;
type: 'P' | 'E' | 'U'; type: "P" | "E" | "U";
}; };
items: OnePuxItem[]; items: OnePuxItem[];
}; };
export type OnePuxAccount = { export type OnePuxAccount = {
attrs: { attrs: {
accountName: string; accountName: string;
name: string; name: string;
avatar: string; avatar: string;
email: string; email: string;
uuid: string; uuid: string;
domain: string; domain: string;
}; };
vaults: OnePuxVault[]; vaults: OnePuxVault[];
}; };
export type OnePuxData = { export type OnePuxData = {
accounts: OnePuxAccount[]; accounts: OnePuxAccount[];
}; };
export type OnePuxAttributes = { export type OnePuxAttributes = {
version: number; version: number;
description: string; description: string;
createdAt: number; createdAt: number;
}; };
export type OnePuxExport = { export type OnePuxExport = {
attributes: OnePuxAttributes; attributes: OnePuxAttributes;
data: OnePuxData; data: OnePuxData;
}; };
export const parse1PuxFile = async ( export async function parse1PuxFile(fileContents: ArrayBuffer): Promise<OnePuxExport> {
fileContents: string | ArrayBuffer, try {
) => { const zip = await loadAsync(fileContents);
try {
const zip = await loadAsync(fileContents);
const attributesContent = await zip const attributesContent = await zip.file("export.attributes")!.async("string");
.file('export.attributes')! const attributes = JSON.parse(attributesContent);
.async('string'); const dataContent = await zip.file("export.data")!.async("string");
const attributes = JSON.parse(attributesContent); const data = JSON.parse(dataContent);
const dataContent = await zip.file('export.data')!.async('string');
const data = JSON.parse(dataContent);
return { return {
attributes, attributes,
data, data,
} as OnePuxExport; } as OnePuxExport;
} catch (error) { } catch (error) {
console.error('Failed to parse .1pux file'); console.error("Failed to parse .1pux file");
throw error; throw error;
} }
}; }
type RowData = { type RowData = {
name: string; name: string;
tags: string; tags: string;
url: string; url: string;
username: string; username: string;
password: string; password: string;
notes: string; notes: string;
extraFields: ExtraField[]; extraFields: ExtraField[];
}; };
type ExtraFieldType = type ExtraFieldType =
| 'username' | "username"
| 'password' | "password"
| 'url' | "url"
| 'email' | "email"
| 'date' | "date"
| 'month' | "month"
| 'credit' | "credit"
| 'phone' | "phone"
| 'totp' | "totp"
| 'text'; | "text";
type ExtraField = { name: string; value: string; type: ExtraFieldType }; type ExtraField = { name: string; value: string; type: ExtraFieldType };
type ParseFieldTypeToExtraFieldType = ( function parseFieldTypeToExtraFieldType(field: OnePuxItemDetailsLoginField): ExtraFieldType {
field: OnePuxItemDetailsLoginField, if (field.designation === "username") {
) => ExtraFieldType; return "username";
} else if (field.designation === "password") {
const parseFieldTypeToExtraFieldType: ParseFieldTypeToExtraFieldType = ( return "password";
field, } else if (field.fieldType === "E") {
) => { return "email";
if (field.designation === 'username') { } else if (field.fieldType === "U") {
return 'username'; return "url";
} else if (field.designation === 'password') {
return 'password';
} else if (field.fieldType === 'E') {
return 'email';
} else if (field.fieldType === 'U') {
return 'url';
}
return 'text';
};
export const parseToRowData = (
item: OnePuxItem['item'],
defaultTags?: string[],
) => {
if (!item) {
return;
}
const rowData: RowData = {
name: item.overview.title,
tags: [...(defaultTags || []), ...(item.overview.tags || [])].join(','),
url: item.overview.url || '',
username: '',
password: '',
notes: item.details.notesPlain || '',
extraFields: [],
};
// Skip documents
if (
item.details.documentAttributes &&
item.details.loginFields.length === 0
) {
return;
}
// Extract username, password, and some extraFields
item.details.loginFields.forEach((field) => {
if (field.designation === 'username') {
rowData.username = field.value;
} else if (field.designation === 'password') {
rowData.password = field.value;
} else if (
field.fieldType === 'I' ||
field.fieldType === 'C' ||
field.id.includes(';opid=__') ||
field.value === ''
) {
// Skip these noisy form-fields
return;
} else {
rowData.extraFields.push({
name: field.name || field.id,
value: field.value,
type: parseFieldTypeToExtraFieldType(field),
});
} }
}); return "text";
}
// Extract some more extraFields export function parseToRowData(item: OnePuxItem["item"], defaultTags?: string[]): RowData | undefined {
item.details.sections.forEach((section) => { if (!item) {
section.fields.forEach((field) => { return;
let value = ''; }
let type: ExtraFieldType = 'text';
if (Object.prototype.hasOwnProperty.call(field.value, 'concealed')) { const rowData: RowData = {
value = field.value.concealed || ''; name: item.overview.title,
} else if ( tags: [...(defaultTags || []), ...(item.overview.tags || [])].join(","),
Object.prototype.hasOwnProperty.call(field.value, 'reference') url: item.overview.url || "",
) { username: "",
value = field.value.reference || ''; password: "",
} else if (Object.prototype.hasOwnProperty.call(field.value, 'string')) { notes: item.details.notesPlain || "",
value = field.value.string || ''; extraFields: [],
} else if (Object.prototype.hasOwnProperty.call(field.value, 'email')) { };
value = field.value.email || '';
type = 'email';
} else if (Object.prototype.hasOwnProperty.call(field.value, 'phone')) {
value = field.value.phone || '';
type = 'phone';
} else if (Object.prototype.hasOwnProperty.call(field.value, 'url')) {
value = field.value.url || '';
type = 'url';
} else if (Object.prototype.hasOwnProperty.call(field.value, 'totp')) {
value = field.value.totp || '';
type = 'totp';
} else if (Object.prototype.hasOwnProperty.call(field.value, 'gender')) {
value = field.value.gender || '';
} else if (
Object.prototype.hasOwnProperty.call(field.value, 'creditCardType')
) {
value = field.value.creditCardType || '';
} else if (
Object.prototype.hasOwnProperty.call(field.value, 'creditCardNumber')
) {
value = field.value.creditCardNumber || '';
type = 'credit';
} else if (
Object.prototype.hasOwnProperty.call(field.value, 'monthYear')
) {
value =
(field.value.monthYear && field.value.monthYear.toString()) || '';
type = 'month';
} else if (Object.prototype.hasOwnProperty.call(field.value, 'date')) {
value = (field.value.date && field.value.date.toString()) || '';
type = 'date';
} else {
// Default, so no data is lost when something new comes up
value = JSON.stringify(field.value);
}
rowData.extraFields.push({ // Skip documents
name: field.title || field.id, if (item.details.documentAttributes && item.details.loginFields.length === 0) {
value, return;
type, }
});
// Extract username, password, and some extraFields
item.details.loginFields.forEach((field) => {
if (field.designation === "username") {
rowData.username = field.value;
} else if (field.designation === "password") {
rowData.password = field.value;
} else if (
field.fieldType === "I" ||
field.fieldType === "C" ||
field.id.includes(";opid=__") ||
field.value === ""
) {
// Skip these noisy form-fields
return;
} else {
rowData.extraFields.push({
name: field.name || field.id,
value: field.value,
type: parseFieldTypeToExtraFieldType(field),
});
}
}); });
});
return rowData; // Extract some more extraFields
}; item.details.sections.forEach((section) => {
section.fields.forEach((field) => {
let value = "";
let type: ExtraFieldType = "text";
if (field.value.concealed) {
value = field.value.concealed || "";
} else if (field.value.reference) {
value = field.value.reference || "";
} else if (field.value.string) {
value = field.value.string || "";
} else if (field.value.email) {
value = field.value.email || "";
type = "email";
} else if (field.value.phone) {
value = field.value.phone || "";
type = "phone";
} else if (field.value.url) {
value = field.value.url || "";
type = "url";
} else if (field.value.totp) {
value = field.value.totp || "";
type = "totp";
} else if (field.value.gender) {
value = field.value.gender || "";
} else if (field.value.creditCardType) {
value = field.value.creditCardType || "";
} else if (field.value.creditCardNumber) {
value = field.value.creditCardNumber || "";
type = "credit";
} else if (field.value.monthYear) {
value = (field.value.monthYear && field.value.monthYear.toString()) || "";
type = "month";
} else if (field.value.date) {
value = (field.value.date && field.value.date.toString()) || "";
type = "date";
} else {
// Default, so no data is lost when something new comes up
value = JSON.stringify(field.value);
}
rowData.extraFields.push({
name: field.title || field.id,
value,
type,
});
});
});
return rowData;
}

View File

@ -7,7 +7,7 @@ const defaults = {
initialDelay: 0, initialDelay: 0,
fullDuration: 1000, fullDuration: 1000,
clear: false, clear: false,
direction: "normal" direction: "normal",
}; };
const clearAnimation = new Map<HTMLElement, number>(); const clearAnimation = new Map<HTMLElement, number>();
@ -20,10 +20,13 @@ export function animateElement(el: HTMLElement, opts = {}) {
el.style.animation = `${animation} ${direction} ${duration}ms ${easing} ${delay}ms ${fill}`; el.style.animation = `${animation} ${direction} ${duration}ms ${easing} ${delay}ms ${fill}`;
if (clear) { if (clear) {
const clearDelay = typeof clear === "number" ? clear : 0; const clearDelay = typeof clear === "number" ? clear : 0;
clearAnimation.set(el, window.setTimeout(() => (el.style.animation = ""), delay + duration + clearDelay)); clearAnimation.set(
el,
window.setTimeout(() => (el.style.animation = ""), delay + duration + clearDelay)
);
} }
return new Promise(resolve => setTimeout(resolve, delay + duration)); return new Promise((resolve) => setTimeout(resolve, delay + duration));
} }
export function animateCascade(nodes: Iterable<Node | Element>, opts = {}) { export function animateCascade(nodes: Iterable<Node | Element>, opts = {}) {

View File

@ -5,8 +5,9 @@ import { VaultItem, Field, createVaultItem, FieldType } from "@padloc/core/src/i
import { Err, ErrorCode } from "@padloc/core/src/error"; import { Err, ErrorCode } from "@padloc/core/src/error";
import { uuid } from "@padloc/core/src/util"; import { uuid } from "@padloc/core/src/util";
import { translate as $l } from "@padloc/locale/src/translate"; import { translate as $l } from "@padloc/locale/src/translate";
import { readFileAsText, readFileAsArrayBuffer } from "@padloc/core/src/attachment";
import { parse1PuxFile, parseToRowData, OnePuxItem } from "./1pux-parser"; import { OnePuxItem } from "./1pux-parser";
export interface ImportFormat { export interface ImportFormat {
value: "csv" | "padlock-legacy" | "lastpass" | "padloc" | "1pux"; value: "csv" | "padlock-legacy" | "lastpass" | "padloc" | "1pux";
@ -101,27 +102,27 @@ export async function isCSV(data: string): Promise<Boolean> {
return papa.parse(data).errors.length === 0; return papa.parse(data).errors.length === 0;
} }
export async function asCSV(data: string, nameColIndex?: number, tagsColIndex?: number): Promise<VaultItem[]> { export async function asCSV(file: File, nameColIndex?: number, tagsColIndex?: number): Promise<VaultItem[]> {
const data = await readFileAsText(file);
const papa = await loadPapa(); const papa = await loadPapa();
const parsed = papa.parse(data); const parsed = papa.parse(data);
if (parsed.errors.length) { if (parsed.errors.length) {
throw new Err(ErrorCode.INVALID_CSV); throw new Err(ErrorCode.INVALID_CSV, "Failed to parse .csv file.");
} }
return fromTable(parsed.data, nameColIndex, tagsColIndex); return fromTable(parsed.data, nameColIndex, tagsColIndex);
} }
/** export async function isPadlockV1(file: File): Promise<boolean> {
* Checks if a given string represents a Padlock enrypted backup
*/
export function isPadlockV1(data: string): boolean {
try { try {
const data = await readFileAsText(file);
return validateLegacyContainer(unmarshal(data)); return validateLegacyContainer(unmarshal(data));
} catch (e) { } catch (e) {
return false; return false;
} }
} }
export async function asPadlockLegacy(data: string, password: string): Promise<VaultItem[]> { export async function asPadlockLegacy(file: File, password: string): Promise<VaultItem[]> {
const data = await readFileAsText(file);
const container = parseLegacyContainer(unmarshal(data)); const container = parseLegacyContainer(unmarshal(data));
await container.unlock(password); await container.unlock(password);
return importLegacyContainer(container); return importLegacyContainer(container);
@ -146,16 +147,18 @@ export async function importLegacyContainer(container: PBES2Container) {
return Promise.all(items); return Promise.all(items);
} }
export function isPBES2Container(data: string) { export async function isPBES2Container(file: File) {
try { try {
const data = await readFileAsText(file);
new PBES2Container().fromRaw(unmarshal(data)); new PBES2Container().fromRaw(unmarshal(data));
return true; return true;
} catch (e) { } catch (error) {
return false; return false;
} }
} }
export async function asPBES2Container(data: string, password: string): Promise<VaultItem[]> { export async function asPBES2Container(file: File, password: string): Promise<VaultItem[]> {
const data = await readFileAsText(file);
const container = new PBES2Container().fromRaw(unmarshal(data)); const container = new PBES2Container().fromRaw(unmarshal(data));
await container.unlock(password); await container.unlock(password);
@ -234,7 +237,8 @@ async function lpParseRow(row: string[]): Promise<VaultItem> {
return createVaultItem(row[nameIndex], fields, dir ? [dir] : []); return createVaultItem(row[nameIndex], fields, dir ? [dir] : []);
} }
export async function asLastPass(data: string): Promise<VaultItem[]> { export async function asLastPass(file: File): Promise<VaultItem[]> {
const data = await readFileAsText(file);
const papa = await loadPapa(); const papa = await loadPapa();
let items = papa let items = papa
.parse(data) .parse(data)
@ -247,22 +251,26 @@ export async function asLastPass(data: string): Promise<VaultItem[]> {
return Promise.all(items); return Promise.all(items);
} }
/** export async function isLastPass(file: File): Promise<boolean> {
* Checks if a given string represents a LastPass CSV file
*/
export function isLastPass(data: string): boolean {
try { try {
const data = await readFileAsText(file);
return data.split("\n")[0] === "url,username,password,extra,name,grouping,fav"; return data.split("\n")[0] === "url,username,password,extra,name,grouping,fav";
} catch (error) { } catch (error) {
return false; return false;
} }
} }
async function parse1PuxItem(accountName: string, vaultName: string, item: OnePuxItem['item']): Promise<VaultItem | undefined> { async function parse1PuxItem(
accountName: string,
vaultName: string,
item: OnePuxItem["item"]
): Promise<VaultItem | undefined> {
if (!item) { if (!item) {
return; return;
} }
const { parseToRowData } = await import("./1pux-parser");
const rowData = parseToRowData(item, [accountName, vaultName]); const rowData = parseToRowData(item, [accountName, vaultName]);
if (!rowData) { if (!rowData) {
@ -270,10 +278,10 @@ async function parse1PuxItem(accountName: string, vaultName: string, item: OnePu
} }
const itemName = rowData.name; const itemName = rowData.name;
const tags = rowData.tags.split(','); const tags = rowData.tags.split(",");
if (item.trashed) { if (item.trashed) {
tags.push('trashed'); tags.push("trashed");
} }
let fields: Field[] = [ let fields: Field[] = [
@ -287,10 +295,10 @@ async function parse1PuxItem(accountName: string, vaultName: string, item: OnePu
} }
for (const extraField of rowData.extraFields) { for (const extraField of rowData.extraFields) {
if (extraField.type === 'totp') { if (extraField.type === "totp") {
// Extract just the secret // Extract just the secret
try { try {
const secret = new URL(extraField.value).searchParams.get('secret'); const secret = new URL(extraField.value).searchParams.get("secret");
if (secret) { if (secret) {
fields.push(new Field({ name: extraField.name, value: secret, type: FieldType.Totp })); fields.push(new Field({ name: extraField.name, value: secret, type: FieldType.Totp }));
} }
@ -298,24 +306,23 @@ async function parse1PuxItem(accountName: string, vaultName: string, item: OnePu
// Do nothing // Do nothing
} }
} else { } else {
fields.push(new Field({ name: extraField.name, value: extraField.value, type: extraField.type as FieldType })); fields.push(
new Field({ name: extraField.name, value: extraField.value, type: extraField.type as FieldType })
);
} }
} }
return createVaultItem(itemName, fields, tags); return createVaultItem(itemName, fields, tags);
} }
export async function as1Pux(data: null | string | ArrayBuffer): Promise<VaultItem[]> { export async function as1Pux(file: File): Promise<VaultItem[]> {
if (!data) {
throw new Err(ErrorCode.INVALID_1PUX);
}
try { try {
const { parse1PuxFile } = await import("./1pux-parser");
const data = await readFileAsArrayBuffer(file);
const dataExport = await parse1PuxFile(data); const dataExport = await parse1PuxFile(data);
const items = []; const items = [];
for (const account of dataExport.data.accounts) { for (const account of dataExport.data.accounts) {
for (const vault of account.vaults) { for (const vault of account.vaults) {
for (const vaultItem of vault.items) { for (const vaultItem of vault.items) {
@ -331,7 +338,7 @@ export async function as1Pux(data: null | string | ArrayBuffer): Promise<VaultIt
return items; return items;
} catch (error) { } catch (error) {
throw new Err(ErrorCode.INVALID_1PUX); throw new Err(ErrorCode.INVALID_1PUX, "Failed to parse .1pux file.");
} }
} }
@ -339,30 +346,23 @@ export async function as1Pux(data: null | string | ArrayBuffer): Promise<VaultIt
* Checks if a given file name ends with .1pux to avoid trying to parse unnecessarily * Checks if a given file name ends with .1pux to avoid trying to parse unnecessarily
*/ */
export function is1Pux(file: File): boolean { export function is1Pux(file: File): boolean {
return file.name.endsWith('.1pux'); return file.name.endsWith(".1pux");
} }
export function guessFormat(file: File, data: string | null | ArrayBuffer): ImportFormat { export async function guessFormat(file: File): Promise<ImportFormat> {
if (isPBES2Container(data as string)) { // Try to guess sync first (won't need parsing)
return PBES2;
}
if (isPadlockV1(data as string)) {
return PADLOCK_LEGACY;
}
if (isLastPass(data as string)) {
return LASTPASS;
}
if (is1Pux(file)) { if (is1Pux(file)) {
return ONEPUX; return ONEPUX;
} }
if (await isPBES2Container(file)) {
return CSV; return PBES2;
} }
if (await isPadlockV1(file)) {
export function doesFileRequireReadingAsBinary(file: File): boolean { return PADLOCK_LEGACY;
if (is1Pux(file)) { }
return true; if (await isLastPass(file)) {
return LASTPASS;
} }
return false; return CSV;
} }

View File

@ -1,7 +1,7 @@
<html> <html>
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes"> <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes" />
<title>lit-element tests</title> <title>lit-element tests</title>
<link href="../node_modules/mocha/mocha.css" rel="stylesheet" /> <link href="../node_modules/mocha/mocha.css" rel="stylesheet" />
<script src="../node_modules/mocha/mocha.js"></script> <script src="../node_modules/mocha/mocha.js"></script>
@ -9,7 +9,7 @@
<script> <script>
mocha.setup({ mocha.setup({
ui: "tdd", ui: "tdd",
timeout: 1000000 timeout: 1000000,
}); });
</script> </script>
<script type="module" src="dist/crypto.js"></script> <script type="module" src="dist/crypto.js"></script>

View File

@ -1,5 +1,5 @@
interface Navigator { interface Navigator {
Backbutton: any Backbutton: any;
} }
interface Window { interface Window {

View File

@ -1,82 +1,82 @@
{ {
"name": "app.padloc", "name": "app.padloc",
"version": "4.0.0", "version": "4.0.0",
"displayName": "Padloc", "displayName": "Padloc",
"private": true, "private": true,
"engines": { "engines": {
"node": "16.13.1", "node": "16.13.1",
"npm": "8.2.0" "npm": "8.2.0"
}, },
"cordova": { "cordova": {
"platforms": [ "platforms": [
"ios", "ios",
"android" "android"
], ],
"plugins": { "plugins": {
"cordova-plugin-app-version": {}, "cordova-plugin-app-version": {},
"cordova-plugin-backbutton": {}, "cordova-plugin-backbutton": {},
"ionic-plugin-keyboard": {}, "ionic-plugin-keyboard": {},
"cordova-plugin-statusbar": {}, "cordova-plugin-statusbar": {},
"cordova-clipboard": {}, "cordova-clipboard": {},
"cordova-plugin-qrscanner": {}, "cordova-plugin-qrscanner": {},
"cordova-plugin-device": {}, "cordova-plugin-device": {},
"cordova-plugin-x-socialsharing": { "cordova-plugin-x-socialsharing": {
"ANDROID_SUPPORT_V4_VERSION": "24.1.1+", "ANDROID_SUPPORT_V4_VERSION": "24.1.1+",
"PHOTO_LIBRARY_ADD_USAGE_DESCRIPTION": "This app requires photo library access to function properly.", "PHOTO_LIBRARY_ADD_USAGE_DESCRIPTION": "This app requires photo library access to function properly.",
"PHOTO_LIBRARY_USAGE_DESCRIPTION": "This app requires photo library access to function properly." "PHOTO_LIBRARY_USAGE_DESCRIPTION": "This app requires photo library access to function properly."
}, },
"cordova-plugin-splashscreen": {}, "cordova-plugin-splashscreen": {},
"cordova-plugin-ionic-webview": { "cordova-plugin-ionic-webview": {
"ANDROID_SUPPORT_ANNOTATIONS_VERSION": "27.+" "ANDROID_SUPPORT_ANNOTATIONS_VERSION": "27.+"
}, },
"cordova-plugin-fingerprint-aio": { "cordova-plugin-fingerprint-aio": {
"FACEID_USAGE_DESCRIPTION": " " "FACEID_USAGE_DESCRIPTION": " "
}, },
"cordova-plugin-privacyscreen": {}, "cordova-plugin-privacyscreen": {},
"cordova-plugin-inappbrowser": {}, "cordova-plugin-inappbrowser": {},
"cordova-plugin-androidx-adapter": {} "cordova-plugin-androidx-adapter": {}
}
},
"dependencies": {
"@padloc/app": "4.0.0",
"@padloc/core": "4.0.0",
"cordova-clipboard": "1.3.0",
"cordova-ios": "6.2.0",
"cordova-plugin-add-swift-support": "2.0.2",
"cordova-plugin-app-version": "~0.1.12",
"cordova-plugin-backbutton": "~0.3.0",
"cordova-plugin-device": "2.0.3",
"cordova-plugin-inappbrowser": "5.0.0",
"cordova-plugin-ionic-webview": "5.0.0",
"cordova-plugin-privacyscreen": "0.4.0",
"cordova-plugin-qrscanner": "3.0.1",
"cordova-plugin-splashscreen": "6.0.0",
"cordova-plugin-statusbar": "2.4.3",
"cordova-plugin-x-socialsharing": "6.0.3",
"cordova-plugin-androidx-adapter": "1.1.3",
"cordova-plugin-fingerprint-aio": "4.0.2",
"es6-promise-plugin": "4.2.2",
"ionic-plugin-keyboard": "~2.2.1",
"typescript": "4.4.3"
},
"devDependencies": {
"@types/cordova-plugin-qrscanner": "1.0.31",
"clean-webpack-plugin": "3.0.0",
"cordova": "10.0.0",
"cordova-android": "10.1.0",
"css-loader": "5.2.6",
"file-loader": "6.2.0",
"html-webpack-plugin": "5.3.1",
"style-loader": "2.0.0",
"ts-loader": "9.2.2",
"ts-node": "10.0.0",
"webpack": "5.38.1",
"webpack-cli": "4.7.0",
"sharp": "0.29.1",
"raw-loader": "4.0.2"
},
"scripts": {
"build": "webpack",
"start": "npm run build && cordova run"
} }
},
"dependencies": {
"@padloc/app": "4.0.0",
"@padloc/core": "4.0.0",
"cordova-clipboard": "1.3.0",
"cordova-ios": "6.2.0",
"cordova-plugin-add-swift-support": "2.0.2",
"cordova-plugin-app-version": "~0.1.12",
"cordova-plugin-backbutton": "~0.3.0",
"cordova-plugin-device": "2.0.3",
"cordova-plugin-inappbrowser": "5.0.0",
"cordova-plugin-ionic-webview": "5.0.0",
"cordova-plugin-privacyscreen": "0.4.0",
"cordova-plugin-qrscanner": "3.0.1",
"cordova-plugin-splashscreen": "6.0.0",
"cordova-plugin-statusbar": "2.4.3",
"cordova-plugin-x-socialsharing": "6.0.3",
"cordova-plugin-androidx-adapter": "1.1.3",
"cordova-plugin-fingerprint-aio": "4.0.2",
"es6-promise-plugin": "4.2.2",
"ionic-plugin-keyboard": "~2.2.1",
"typescript": "4.4.3"
},
"devDependencies": {
"@types/cordova-plugin-qrscanner": "1.0.31",
"clean-webpack-plugin": "3.0.0",
"cordova": "10.0.0",
"cordova-android": "10.1.0",
"css-loader": "5.2.6",
"file-loader": "6.2.0",
"html-webpack-plugin": "5.3.1",
"style-loader": "2.0.0",
"ts-loader": "9.2.2",
"ts-node": "10.0.0",
"webpack": "5.38.1",
"webpack-cli": "4.7.0",
"sharp": "0.29.1",
"raw-loader": "4.0.2"
},
"scripts": {
"build": "webpack",
"start": "npm run build && cordova run"
}
} }

View File

@ -13,4 +13,3 @@
"include": ["src/**/*.ts", "../app/types/*.ts"], "include": ["src/**/*.ts", "../app/types/*.ts"],
"exclude": ["node_modules/**/*.ts"] "exclude": ["node_modules/**/*.ts"]
} }

View File

@ -105,24 +105,7 @@ module.exports = {
.toBuffer(); .toBuffer();
const iosIconSizes = [ const iosIconSizes = [
20, 20, 29, 40, 50, 57, 58, 60, 72, 76, 80, 87, 100, 114, 120, 144, 152, 167, 180,
29,
40,
50,
57,
58,
60,
72,
76,
80,
87,
100,
114,
120,
144,
152,
167,
180,
]; ];
const androidIconSizes = [36, 48, 72, 96, 144, 192]; const androidIconSizes = [36, 48, 72, 96, 144, 192];

View File

@ -1,38 +1,38 @@
{ {
"name": "@padloc/core", "name": "@padloc/core",
"version": "4.0.0", "version": "4.0.0",
"description": "padloc core module", "description": "padloc core module",
"main": "index.js", "main": "index.js",
"author": "Martin Kleinschrodt <martin@maklesoft.com>", "author": "Martin Kleinschrodt <martin@maklesoft.com>",
"license": "GPLv3", "license": "GPLv3",
"files": [ "files": [
"src", "src",
"vendor", "vendor",
"tsconfig.json" "tsconfig.json"
], ],
"dependencies": { "dependencies": {
"@padloc/locale": "4.0.0", "@padloc/locale": "4.0.0",
"typescript": "4.4.3" "typescript": "4.4.3"
}, },
"devDependencies": { "devDependencies": {
"@types/chai": "4.2.18", "@types/chai": "4.2.18",
"@types/mocha": "8.2.2", "@types/mocha": "8.2.2",
"chai": "4.3.4", "chai": "4.3.4",
"mocha": "8.4.0", "mocha": "8.4.0",
"ts-node": "10.0.0", "ts-node": "10.0.0",
"typedoc": "0.22.4" "typedoc": "0.22.4"
}, },
"scripts": { "scripts": {
"test": "cd test && mocha -r ts-node/register *.ts", "test": "cd test && mocha -r ts-node/register *.ts",
"docs": "typedoc --mode modules --out docs" "docs": "typedoc --mode modules --out docs"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/padloc/padloc.git", "url": "https://github.com/padloc/padloc.git",
"directory": "packages/core" "directory": "packages/core"
}, },
"engines": { "engines": {
"node": "16.13.1", "node": "16.13.1",
"npm": "8.2.0" "npm": "8.2.0"
} }
} }

View File

@ -6,7 +6,7 @@ import { getCryptoProvider as getProvider } from "./platform";
import { Err, ErrorCode } from "./error"; import { Err, ErrorCode } from "./error";
import { RequestProgress } from "./transport"; import { RequestProgress } from "./transport";
function readFile(blob: File): Promise<Uint8Array> { export async function readFileAsUint8Array(blob: File): Promise<Uint8Array> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const reader = new FileReader(); const reader = new FileReader();
@ -15,16 +15,33 @@ function readFile(blob: File): Promise<Uint8Array> {
resolve(result); resolve(result);
}; };
reader.onerror = e => { reader.onerror = (error) => {
reader.abort(); reader.abort();
reject(e); reject(error);
}; };
reader.readAsArrayBuffer(blob); reader.readAsArrayBuffer(blob);
}); });
} }
function readAsText(blob: File): Promise<string> { export async function readFileAsArrayBuffer(blob: File): Promise<ArrayBuffer> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
resolve(reader.result as ArrayBuffer);
};
reader.onerror = (error) => {
reader.abort();
reject(error);
};
reader.readAsArrayBuffer(blob);
});
}
export async function readFileAsText(blob: File): Promise<string> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const reader = new FileReader(); const reader = new FileReader();
@ -32,9 +49,9 @@ function readAsText(blob: File): Promise<string> {
resolve(reader.result as string); resolve(reader.result as string);
}; };
reader.onerror = e => { reader.onerror = (error) => {
reader.abort(); reader.abort();
reject(e); reject(error);
}; };
reader.readAsText(blob); reader.readAsText(blob);
@ -49,7 +66,7 @@ function readFileAsDataURL(blob: File): Promise<string> {
resolve(reader.result as string); resolve(reader.result as string);
}; };
reader.onerror = e => { reader.onerror = (e) => {
reader.abort(); reader.abort();
reject(e); reject(e);
}; };
@ -89,7 +106,7 @@ export class Attachment extends SimpleContainer {
super(); super();
Object.assign(this, { Object.assign(this, {
_key: key, _key: key,
...info ...info,
}); });
} }
@ -100,7 +117,7 @@ export class Attachment extends SimpleContainer {
name: this.name, name: this.name,
type: this.type, type: this.type,
size: this.size, size: this.size,
key: this._key key: this._key,
}); });
} }
@ -113,11 +130,11 @@ export class Attachment extends SimpleContainer {
this.size = file.size; this.size = file.size;
this.name = file.name; this.name = file.name;
const data = await readFile(file); const data = await readFileAsUint8Array(file);
this._key = await getProvider().generateKey({ this._key = await getProvider().generateKey({
algorithm: "AES", algorithm: "AES",
keySize: this.encryptionParams.keySize keySize: this.encryptionParams.keySize,
} as AESKeyParams); } as AESKeyParams);
await this.setData(data); await this.setData(data);
@ -141,7 +158,7 @@ export class Attachment extends SimpleContainer {
async toText(): Promise<string> { async toText(): Promise<string> {
const file = await this.toFile(); const file = await this.toFile();
return readAsText(file); return readFileAsText(file);
} }
validate() { validate() {

View File

@ -7,7 +7,7 @@ export enum PlanType {
Premium, Premium,
Family, Family,
Team, Team,
Business Business,
} }
export class Plan extends Serializable { export class Plan extends Serializable {
@ -37,7 +37,7 @@ export enum SubscriptionStatus {
Trialing = "trialing", Trialing = "trialing",
Active = "active", Active = "active",
Inactive = "inactive", Inactive = "inactive",
Canceled = "canceled" Canceled = "canceled",
} }
export class Subscription extends Serializable { export class Subscription extends Serializable {

View File

@ -41,10 +41,10 @@ export class Client extends API {
method, method,
typeof input === "undefined" ? [] : [input instanceof Serializable ? input.toRaw() : input], typeof input === "undefined" ? [] : [input instanceof Serializable ? input.toRaw() : input],
progress progress
).then(res => { ).then((res) => {
return output return output
? Array.isArray(res.result) ? Array.isArray(res.result)
? res.result.map(each => new output().fromRaw(each)) ? res.result.map((each) => new output().fromRaw(each))
: new output().fromRaw(res.result) : new output().fromRaw(res.result)
: res.result; : res.result;
}) as PromiseWithProgress<any>; }) as PromiseWithProgress<any>;
@ -85,7 +85,7 @@ export class Client extends API {
} }
if (res.error) { if (res.error) {
const err = new Err((res.error.code as any) as ErrorCode, res.error.message); const err = new Err(res.error.code as any as ErrorCode, res.error.message);
if (progress) { if (progress) {
progress.error = err; progress.error = err;
} }

View File

@ -31,7 +31,7 @@ export class VaultItemCollection extends Serializable implements Iterable<VaultI
constructor(items: VaultItem[] = []) { constructor(items: VaultItem[] = []) {
super(); super();
this._items = new Map(items.map(item => [item.id, item] as [string, VaultItem])); this._items = new Map(items.map((item) => [item.id, item] as [string, VaultItem]));
} }
/** Get an item with a given `id` */ /** Get an item with a given `id` */
@ -91,8 +91,8 @@ export class VaultItemCollection extends Serializable implements Iterable<VaultI
protected _toRaw(version: string) { protected _toRaw(version: string) {
return { return {
items: Array.from(this).map(item => item.toRaw(version)), items: Array.from(this).map((item) => item.toRaw(version)),
changes: [...this._changes] changes: [...this._changes],
}; };
} }

View File

@ -65,15 +65,15 @@ export function parseLegacyContainer(raw: LegacyContainer): PBES2Container {
tagSize: raw.ts, tagSize: raw.ts,
keySize: raw.keySize, keySize: raw.keySize,
iv: raw.iv, iv: raw.iv,
additionalData: raw.adata additionalData: raw.adata,
}, },
keyParams: { keyParams: {
algorithm: "PBKDF2", algorithm: "PBKDF2",
hash: "SHA-256", hash: "SHA-256",
keySize: raw.keySize, keySize: raw.keySize,
iterations: raw.iter, iterations: raw.iter,
salt: raw.salt salt: raw.salt,
}, },
encryptedData: raw.ct encryptedData: raw.ct,
}); });
} }

View File

@ -20,22 +20,22 @@ export const MIGRATIONS: Migration[] = [
up: ({ mainVault, orgs, ...rest }: any) => ({ up: ({ mainVault, orgs, ...rest }: any) => ({
mainVault: { id: mainVault }, mainVault: { id: mainVault },
orgs: orgs.map((org: any) => ({ orgs: orgs.map((org: any) => ({
id: org id: org,
})), })),
...rest ...rest,
}), }),
down: ({ mainVault, orgs, ...rest }: any) => ({ down: ({ mainVault, orgs, ...rest }: any) => ({
mainVault: mainVault.id, mainVault: mainVault.id,
orgs: orgs.map((org: any) => org.id), orgs: orgs.map((org: any) => org.id),
...rest ...rest,
}) }),
}, },
all: { all: {
up: (raw: any, kind?: string) => ({ kind, ...raw }), up: (raw: any, kind?: string) => ({ kind, ...raw }),
down: ({ kind, ...rest }) => rest down: ({ kind, ...rest }) => rest,
} },
} },
} },
]; ];
export const EARLIEST_VERSION = MIGRATIONS[0].from; export const EARLIEST_VERSION = MIGRATIONS[0].from;
@ -44,7 +44,7 @@ export const LATEST_VERSION = MIGRATIONS[MIGRATIONS.length - 1].to;
function norm(version: string = EARLIEST_VERSION): string { function norm(version: string = EARLIEST_VERSION): string {
return version return version
.split(".") .split(".")
.map(part => part.padStart(3, "0")) .map((part) => part.padStart(3, "0"))
.join(); .join();
} }
@ -58,7 +58,7 @@ export function upgrade(kind: string, raw: any, version: string = LATEST_VERSION
} }
const migration = MIGRATIONS.find( const migration = MIGRATIONS.find(
m => norm(m.from) >= norm(raw.version || EARLIEST_VERSION) && norm(m.to) <= norm(version) (m) => norm(m.from) >= norm(raw.version || EARLIEST_VERSION) && norm(m.to) <= norm(version)
); );
if (migration) { if (migration) {
@ -76,7 +76,7 @@ export function upgrade(kind: string, raw: any, version: string = LATEST_VERSION
export function downgrade(kind: string, raw: any, version: string = LATEST_VERSION): any { export function downgrade(kind: string, raw: any, version: string = LATEST_VERSION): any {
const migration = MIGRATIONS.reverse().find( const migration = MIGRATIONS.reverse().find(
m => norm(m.to) <= norm(raw.version || LATEST_VERSION) && norm(m.from) >= norm(version) (m) => norm(m.to) <= norm(raw.version || LATEST_VERSION) && norm(m.from) >= norm(version)
); );
if (migration) { if (migration) {

View File

@ -115,9 +115,7 @@ export interface Platform {
authenticatorIndex?: number; authenticatorIndex?: number;
}): Promise<StartAuthRequestResponse>; }): Promise<StartAuthRequestResponse>;
completeAuthRequest( completeAuthRequest(req: StartAuthRequestResponse): Promise<{
req: StartAuthRequestResponse
): Promise<{
token: string; token: string;
accountStatus: AccountStatus; accountStatus: AccountStatus;
deviceTrusted: boolean; deviceTrusted: boolean;
@ -193,9 +191,7 @@ export class StubPlatform implements Platform {
throw new Error("Method not implemented."); throw new Error("Method not implemented.");
} }
async completeAuthRequest( async completeAuthRequest(_req: StartAuthRequestResponse): Promise<{
_req: StartAuthRequestResponse
): Promise<{
token: string; token: string;
accountStatus: AccountStatus; accountStatus: AccountStatus;
deviceTrusted: boolean; deviceTrusted: boolean;
@ -293,9 +289,7 @@ export function startAuthRequest(opts: {
return platform.startAuthRequest(opts); return platform.startAuthRequest(opts);
} }
export function completeAuthRequest( export function completeAuthRequest(req: StartAuthRequestResponse): Promise<{
req: StartAuthRequestResponse
): Promise<{
token: string; token: string;
accountStatus: AccountStatus; accountStatus: AccountStatus;
deviceTrusted: boolean; deviceTrusted: boolean;

View File

@ -30,7 +30,7 @@ type Position = [number, number];
export const defaults: Options = { export const defaults: Options = {
height: 9, height: 9,
width: 17, width: 17,
symbols: [" ", ".", "o", "+", "=", "*", "B", "O", "X", "@", "%", "&", "#", "/", "^", "S", "E"] symbols: [" ", ".", "o", "+", "=", "*", "B", "O", "X", "@", "%", "&", "#", "/", "^", "S", "E"],
}; };
// Converts a buffer to a binary moves array // Converts a buffer to a binary moves array
@ -120,6 +120,6 @@ export function randomArt(fingerprint: Uint8Array, opts: Partial<Options> = {}):
width: options.width, width: options.width,
height: options.height, height: options.height,
values: vals, values: vals,
symbols: vals.map(line => line.map(val => symbols[val])) symbols: vals.map((line) => line.map((val) => symbols[val])),
}; };
} }

View File

@ -75,7 +75,7 @@ const SJCLProvider = {
} catch (e) { } catch (e) {
throw new Err(ErrorCode.ENCRYPTION_FAILED); throw new Err(ErrorCode.ENCRYPTION_FAILED);
} }
} },
}; };
export default SJCLProvider; export default SJCLProvider;

View File

@ -29,12 +29,12 @@ export function appSpec(): Spec {
const user = { const user = {
email: "lengden@olga.com", email: "lengden@olga.com",
name: "Lengden Olga", name: "Lengden Olga",
password: "correct battery horse staple" password: "correct battery horse staple",
}; };
const otherUser = { const otherUser = {
email: "max@mustermann.com", email: "max@mustermann.com",
name: "Max Mustermann", name: "Max Mustermann",
password: "password" password: "password",
}; };
let sharedVaultID = ""; let sharedVaultID = "";
// let otherVaultID = ""; // let otherVaultID = "";
@ -191,7 +191,7 @@ export function appSpec(): Spec {
test("Simulataneous Edit", async () => { test("Simulataneous Edit", async () => {
const [item1, item2] = await Promise.all([ const [item1, item2] = await Promise.all([
app.createItem("Added Item 1", { id: sharedVaultID }), app.createItem("Added Item 1", { id: sharedVaultID }),
otherApp.createItem("Added Item 2", { id: sharedVaultID }) otherApp.createItem("Added Item 2", { id: sharedVaultID }),
]); ]);
await Promise.all([app.syncVault({ id: sharedVaultID }), otherApp.syncVault({ id: sharedVaultID })]); await Promise.all([app.syncVault({ id: sharedVaultID }), otherApp.syncVault({ id: sharedVaultID })]);

View File

@ -7,7 +7,7 @@ import {
HMACKeyParams, HMACKeyParams,
PBKDF2Params, PBKDF2Params,
RSASigningParams, RSASigningParams,
RSAEncryptionParams RSAEncryptionParams,
} from "../crypto"; } from "../crypto";
import { Spec } from "./spec"; import { Spec } from "./spec";
@ -26,7 +26,7 @@ const fixtures = {
plain: stringToBytes("Hello World!"), plain: stringToBytes("Hello World!"),
encrypted: base64ToBytes( encrypted: base64ToBytes(
"eAJDfWUdgL4Wl0UDsA0WsmHE29MNAnTvSjus3N0BP6foD0fFZBlrfmRbF-KjY_2zYhgaqn7E4pEKMB20tPDC-JYcAJO8PMWOR6PdLBsBCUTbdYy062iwFWgWfzSFV2LDy-G2t9HL2CbDoDAdsh1fNGIm81nY9sXbB0kKM4uNXKTdVl49Cwf30jiRRpABV_tSPmQjkHDVWOphVEY5ex0hhveRC6vfO1YZ21-CuoTa1gRq-ab21V-Pl5rfQ0RHsDgtvvSJ8_3ihzCkOTjd2Anj0GiKEsCeV0NaEgT-e5WyDj2zYNIsVOoMmB65UUkXX002Ycc2cGuoYw2uudZQSaAlqg" "eAJDfWUdgL4Wl0UDsA0WsmHE29MNAnTvSjus3N0BP6foD0fFZBlrfmRbF-KjY_2zYhgaqn7E4pEKMB20tPDC-JYcAJO8PMWOR6PdLBsBCUTbdYy062iwFWgWfzSFV2LDy-G2t9HL2CbDoDAdsh1fNGIm81nY9sXbB0kKM4uNXKTdVl49Cwf30jiRRpABV_tSPmQjkHDVWOphVEY5ex0hhveRC6vfO1YZ21-CuoTa1gRq-ab21V-Pl5rfQ0RHsDgtvvSJ8_3ihzCkOTjd2Anj0GiKEsCeV0NaEgT-e5WyDj2zYNIsVOoMmB65UUkXX002Ycc2cGuoYw2uudZQSaAlqg"
) ),
}, },
aes: { aes: {
key: base64ToBytes("fe8Chv8F4je2wW3u67H6KFVtlRuH4VWBgN1FXPoOKAw"), key: base64ToBytes("fe8Chv8F4je2wW3u67H6KFVtlRuH4VWBgN1FXPoOKAw"),
@ -34,8 +34,8 @@ const fixtures = {
iv: base64ToBytes("Tg9aODkCqoBQDktZAXLEhw"), iv: base64ToBytes("Tg9aODkCqoBQDktZAXLEhw"),
additionalData: base64ToBytes("dJvkr9MnycfsAHiqZStEOg"), additionalData: base64ToBytes("dJvkr9MnycfsAHiqZStEOg"),
plain: stringToBytes("Hello World!"), plain: stringToBytes("Hello World!"),
encrypted: base64ToBytes("A4ODqQsZrMQ4hXxV-RyYVDhdjTvk1MQ7MG8j_A") encrypted: base64ToBytes("A4ODqQsZrMQ4hXxV-RyYVDhdjTvk1MQ7MG8j_A"),
} },
}; };
export function cryptoProviderSpec(provider: CryptoProvider): Spec { export function cryptoProviderSpec(provider: CryptoProvider): Spec {
@ -59,7 +59,7 @@ export function cryptoProviderSpec(provider: CryptoProvider): Spec {
let err; let err;
try { try {
await provider.hash(input, ({ algorithm: "BLAH" } as any) as HashParams); await provider.hash(input, { algorithm: "BLAH" } as any as HashParams);
} catch (e) { } catch (e) {
err = e; err = e;
} }

View File

@ -13,7 +13,7 @@ import {
AESEncryptionParams, AESEncryptionParams,
RSAEncryptionParams, RSAEncryptionParams,
HashParams, HashParams,
RSASigningParams RSASigningParams,
} from "./crypto"; } from "./crypto";
import { concatBytes as concat, equalBytes as equal } from "./encoding"; import { concatBytes as concat, equalBytes as equal } from "./encoding";
import { Err, ErrorCode } from "./error"; import { Err, ErrorCode } from "./error";
@ -50,7 +50,7 @@ export class StubCryptoProvider implements CryptoProvider {
const key = await this.randomBytes(32); const key = await this.randomBytes(32);
return { return {
publicKey: key, publicKey: key,
privateKey: key privateKey: key,
}; };
} }
} }

View File

@ -172,10 +172,10 @@ export class BigInteger {
setBit(n: number): BigInteger; setBit(n: number): BigInteger;
// (public) this & ~(1<<n) // (public) this & ~(1<<n)
clearBit(n: number): BigInteger clearBit(n: number): BigInteger;
// (public) this ^ (1<<n) // (public) this ^ (1<<n)
flipBit(n: number): BigInteger flipBit(n: number): BigInteger;
// (protected) r = this + a // (protected) r = this + a
addTo(a: BigInteger, r: BigInteger): void; addTo(a: BigInteger, r: BigInteger): void;

View File

@ -1369,174 +1369,14 @@ function bnModInverse(m) {
} }
var lowprimes = [ var lowprimes = [
2, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109,
3, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239,
5, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379,
7, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521,
11, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661,
13, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827,
17, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991,
19, 997,
23,
29,
31,
37,
41,
43,
47,
53,
59,
61,
67,
71,
73,
79,
83,
89,
97,
101,
103,
107,
109,
113,
127,
131,
137,
139,
149,
151,
157,
163,
167,
173,
179,
181,
191,
193,
197,
199,
211,
223,
227,
229,
233,
239,
241,
251,
257,
263,
269,
271,
277,
281,
283,
293,
307,
311,
313,
317,
331,
337,
347,
349,
353,
359,
367,
373,
379,
383,
389,
397,
401,
409,
419,
421,
431,
433,
439,
443,
449,
457,
461,
463,
467,
479,
487,
491,
499,
503,
509,
521,
523,
541,
547,
557,
563,
569,
571,
577,
587,
593,
599,
601,
607,
613,
617,
619,
631,
641,
643,
647,
653,
659,
661,
673,
677,
683,
691,
701,
709,
719,
727,
733,
739,
743,
751,
757,
761,
769,
773,
787,
797,
809,
811,
821,
823,
827,
829,
839,
853,
857,
859,
863,
877,
881,
883,
887,
907,
911,
919,
929,
937,
941,
947,
953,
967,
971,
977,
983,
991,
997
]; ];
var lplim = (1 << 26) / lowprimes[lowprimes.length - 1]; var lplim = (1 << 26) / lowprimes[lowprimes.length - 1];

View File

@ -1,2 +1 @@
export var sjcl: any export var sjcl: any;

View File

@ -68,8 +68,8 @@ var sjcl = {
* Ciphertext is corrupt. * Ciphertext is corrupt.
* @constructor * @constructor
*/ */
corrupt: function(message) { corrupt: function (message) {
this.toString = function() { this.toString = function () {
return "CORRUPT: " + this.message; return "CORRUPT: " + this.message;
}; };
this.message = message; this.message = message;
@ -79,8 +79,8 @@ var sjcl = {
* Invalid parameter. * Invalid parameter.
* @constructor * @constructor
*/ */
invalid: function(message) { invalid: function (message) {
this.toString = function() { this.toString = function () {
return "INVALID: " + this.message; return "INVALID: " + this.message;
}; };
this.message = message; this.message = message;
@ -90,8 +90,8 @@ var sjcl = {
* Bug or missing feature in SJCL. * Bug or missing feature in SJCL.
* @constructor * @constructor
*/ */
bug: function(message) { bug: function (message) {
this.toString = function() { this.toString = function () {
return "BUG: " + this.message; return "BUG: " + this.message;
}; };
this.message = message; this.message = message;
@ -101,13 +101,13 @@ var sjcl = {
* Something isn't ready. * Something isn't ready.
* @constructor * @constructor
*/ */
notReady: function(message) { notReady: function (message) {
this.toString = function() { this.toString = function () {
return "NOT READY: " + this.message; return "NOT READY: " + this.message;
}; };
this.message = message; this.message = message;
} },
} },
}; };
/** @fileOverview Low-level AES implementation. /** @fileOverview Low-level AES implementation.
* *
@ -133,7 +133,7 @@ var sjcl = {
* @constructor * @constructor
* @param {Array} key The key as an array of 4, 6 or 8 words. * @param {Array} key The key as an array of 4, 6 or 8 words.
*/ */
sjcl.cipher.aes = function(key) { sjcl.cipher.aes = function (key) {
if (!this._tables[0][0][0]) { if (!this._tables[0][0][0]) {
this._precompute(); this._precompute();
} }
@ -204,7 +204,7 @@ sjcl.cipher.aes.prototype = {
* @param {Array} data The plaintext. * @param {Array} data The plaintext.
* @return {Array} The ciphertext. * @return {Array} The ciphertext.
*/ */
encrypt: function(data) { encrypt: function (data) {
return this._crypt(data, 0); return this._crypt(data, 0);
}, },
@ -213,7 +213,7 @@ sjcl.cipher.aes.prototype = {
* @param {Array} data The ciphertext. * @param {Array} data The ciphertext.
* @return {Array} The plaintext. * @return {Array} The plaintext.
*/ */
decrypt: function(data) { decrypt: function (data) {
return this._crypt(data, 1); return this._crypt(data, 1);
}, },
@ -229,14 +229,17 @@ sjcl.cipher.aes.prototype = {
* *
* @private * @private
*/ */
_tables: [[[], [], [], [], []], [[], [], [], [], []]], _tables: [
[[], [], [], [], []],
[[], [], [], [], []],
],
/** /**
* Expand the S-box tables. * Expand the S-box tables.
* *
* @private * @private
*/ */
_precompute: function() { _precompute: function () {
var encTable = this._tables[0], var encTable = this._tables[0],
decTable = this._tables[1], decTable = this._tables[1],
sbox = encTable[4], sbox = encTable[4],
@ -290,7 +293,7 @@ sjcl.cipher.aes.prototype = {
* @return {Array} The four encrypted or decrypted words. * @return {Array} The four encrypted or decrypted words.
* @private * @private
*/ */
_crypt: function(input, dir) { _crypt: function (input, dir) {
if (input.length !== 4) { if (input.length !== 4) {
throw new sjcl.exception.invalid("invalid aes block size"); throw new sjcl.exception.invalid("invalid aes block size");
} }
@ -344,7 +347,7 @@ sjcl.cipher.aes.prototype = {
} }
return out; return out;
} },
}; };
/** @fileOverview Arrays of bits, encoded as arrays of Numbers. /** @fileOverview Arrays of bits, encoded as arrays of Numbers.
@ -387,7 +390,7 @@ sjcl.bitArray = {
* slice until the end of the array. * slice until the end of the array.
* @return {bitArray} The requested slice. * @return {bitArray} The requested slice.
*/ */
bitSlice: function(a, bstart, bend) { bitSlice: function (a, bstart, bend) {
a = sjcl.bitArray._shiftRight(a.slice(bstart / 32), 32 - (bstart & 31)).slice(1); a = sjcl.bitArray._shiftRight(a.slice(bstart / 32), 32 - (bstart & 31)).slice(1);
return bend === undefined ? a : sjcl.bitArray.clamp(a, bend - bstart); return bend === undefined ? a : sjcl.bitArray.clamp(a, bend - bstart);
}, },
@ -399,7 +402,7 @@ sjcl.bitArray = {
* @param {Number} blength The length of the number to extract. * @param {Number} blength The length of the number to extract.
* @return {Number} The requested slice. * @return {Number} The requested slice.
*/ */
extract: function(a, bstart, blength) { extract: function (a, bstart, blength) {
// FIXME: this Math.floor is not necessary at all, but for some reason // FIXME: this Math.floor is not necessary at all, but for some reason
// seems to suppress a bug in the Chromium JIT. // seems to suppress a bug in the Chromium JIT.
var x, var x,
@ -420,7 +423,7 @@ sjcl.bitArray = {
* @param {bitArray} a2 The second array. * @param {bitArray} a2 The second array.
* @return {bitArray} The concatenation of a1 and a2. * @return {bitArray} The concatenation of a1 and a2.
*/ */
concat: function(a1, a2) { concat: function (a1, a2) {
if (a1.length === 0 || a2.length === 0) { if (a1.length === 0 || a2.length === 0) {
return a1.concat(a2); return a1.concat(a2);
} }
@ -439,7 +442,7 @@ sjcl.bitArray = {
* @param {bitArray} a The array. * @param {bitArray} a The array.
* @return {Number} The length of a, in bits. * @return {Number} The length of a, in bits.
*/ */
bitLength: function(a) { bitLength: function (a) {
var l = a.length, var l = a.length,
x; x;
if (l === 0) { if (l === 0) {
@ -455,7 +458,7 @@ sjcl.bitArray = {
* @param {Number} len The length to truncate to, in bits. * @param {Number} len The length to truncate to, in bits.
* @return {bitArray} A new array, truncated to len bits. * @return {bitArray} A new array, truncated to len bits.
*/ */
clamp: function(a, len) { clamp: function (a, len) {
if (a.length * 32 < len) { if (a.length * 32 < len) {
return a; return a;
} }
@ -475,7 +478,7 @@ sjcl.bitArray = {
* @param {Number} [_end=0] Pass 1 if x has already been shifted to the high side. * @param {Number} [_end=0] Pass 1 if x has already been shifted to the high side.
* @return {Number} The partial word. * @return {Number} The partial word.
*/ */
partial: function(len, x, _end) { partial: function (len, x, _end) {
if (len === 32) { if (len === 32) {
return x; return x;
} }
@ -487,7 +490,7 @@ sjcl.bitArray = {
* @param {Number} x The partial word. * @param {Number} x The partial word.
* @return {Number} The number of bits used by the partial word. * @return {Number} The number of bits used by the partial word.
*/ */
getPartial: function(x) { getPartial: function (x) {
return Math.round(x / 0x10000000000) || 32; return Math.round(x / 0x10000000000) || 32;
}, },
@ -497,7 +500,7 @@ sjcl.bitArray = {
* @param {bitArray} b The second array. * @param {bitArray} b The second array.
* @return {boolean} true if a == b; false otherwise. * @return {boolean} true if a == b; false otherwise.
*/ */
equal: function(a, b) { equal: function (a, b) {
if (sjcl.bitArray.bitLength(a) !== sjcl.bitArray.bitLength(b)) { if (sjcl.bitArray.bitLength(a) !== sjcl.bitArray.bitLength(b)) {
return false; return false;
} }
@ -516,7 +519,7 @@ sjcl.bitArray = {
* @param {bitArray} [out=[]] An array to prepend to the output. * @param {bitArray} [out=[]] An array to prepend to the output.
* @private * @private
*/ */
_shiftRight: function(a, shift, carry, out) { _shiftRight: function (a, shift, carry, out) {
var i, var i,
last2 = 0, last2 = 0,
shift2; shift2;
@ -545,7 +548,7 @@ sjcl.bitArray = {
/** xor a block of 4 words together. /** xor a block of 4 words together.
* @private * @private
*/ */
_xor4: function(x, y) { _xor4: function (x, y) {
return [x[0] ^ y[0], x[1] ^ y[1], x[2] ^ y[2], x[3] ^ y[3]]; return [x[0] ^ y[0], x[1] ^ y[1], x[2] ^ y[2], x[3] ^ y[3]];
}, },
@ -554,7 +557,7 @@ sjcl.bitArray = {
* @param {sjcl.bitArray} a word array * @param {sjcl.bitArray} a word array
* @return {sjcl.bitArray} byteswapped array * @return {sjcl.bitArray} byteswapped array
*/ */
byteswapM: function(a) { byteswapM: function (a) {
var i, var i,
v, v,
m = 0xff00; m = 0xff00;
@ -563,7 +566,7 @@ sjcl.bitArray = {
a[i] = (v >>> 24) | ((v >>> 8) & m) | ((v & m) << 8) | (v << 24); a[i] = (v >>> 24) | ((v >>> 8) & m) | ((v & m) << 8) | (v << 24);
} }
return a; return a;
} },
}; };
/** @fileOverview Bit array codec implementations. /** @fileOverview Bit array codec implementations.
* *
@ -578,7 +581,7 @@ sjcl.bitArray = {
*/ */
sjcl.codec.utf8String = { sjcl.codec.utf8String = {
/** Convert from a bitArray to a UTF-8 string. */ /** Convert from a bitArray to a UTF-8 string. */
fromBits: function(arr) { fromBits: function (arr) {
var out = "", var out = "",
bl = sjcl.bitArray.bitLength(arr), bl = sjcl.bitArray.bitLength(arr),
i, i,
@ -594,7 +597,7 @@ sjcl.codec.utf8String = {
}, },
/** Convert from a UTF-8 string to a bitArray. */ /** Convert from a UTF-8 string to a bitArray. */
toBits: function(str) { toBits: function (str) {
str = unescape(encodeURIComponent(str)); str = unescape(encodeURIComponent(str));
var out = [], var out = [],
i, i,
@ -610,7 +613,7 @@ sjcl.codec.utf8String = {
out.push(sjcl.bitArray.partial(8 * (i & 3), tmp)); out.push(sjcl.bitArray.partial(8 * (i & 3), tmp));
} }
return out; return out;
} },
}; };
/** @fileOverview Bit array codec implementations. /** @fileOverview Bit array codec implementations.
* *
@ -625,7 +628,7 @@ sjcl.codec.utf8String = {
*/ */
sjcl.codec.bytes = { sjcl.codec.bytes = {
/** Convert from a bitArray to an array of bytes. */ /** Convert from a bitArray to an array of bytes. */
fromBits: function(arr) { fromBits: function (arr) {
var out = [], var out = [],
bl = sjcl.bitArray.bitLength(arr), bl = sjcl.bitArray.bitLength(arr),
i, i,
@ -640,7 +643,7 @@ sjcl.codec.bytes = {
return out; return out;
}, },
/** Convert from an array of bytes to a bitArray. */ /** Convert from an array of bytes to a bitArray. */
toBits: function(bytes) { toBits: function (bytes) {
var out = [], var out = [],
i, i,
tmp = 0; tmp = 0;
@ -655,7 +658,7 @@ sjcl.codec.bytes = {
out.push(sjcl.bitArray.partial(8 * (i & 3), tmp)); out.push(sjcl.bitArray.partial(8 * (i & 3), tmp));
} }
return out; return out;
} },
}; };
/** @fileOverview CCM mode implementation. /** @fileOverview CCM mode implementation.
* *
@ -679,18 +682,18 @@ sjcl.mode.ccm = {
_progressListeners: [], _progressListeners: [],
listenProgress: function(cb) { listenProgress: function (cb) {
sjcl.mode.ccm._progressListeners.push(cb); sjcl.mode.ccm._progressListeners.push(cb);
}, },
unListenProgress: function(cb) { unListenProgress: function (cb) {
var index = sjcl.mode.ccm._progressListeners.indexOf(cb); var index = sjcl.mode.ccm._progressListeners.indexOf(cb);
if (index > -1) { if (index > -1) {
sjcl.mode.ccm._progressListeners.splice(index, 1); sjcl.mode.ccm._progressListeners.splice(index, 1);
} }
}, },
_callProgressListener: function(val) { _callProgressListener: function (val) {
var p = sjcl.mode.ccm._progressListeners.slice(), var p = sjcl.mode.ccm._progressListeners.slice(),
i; i;
@ -708,7 +711,7 @@ sjcl.mode.ccm = {
* @param {Number} [tlen=64] the desired tag length, in bits. * @param {Number} [tlen=64] the desired tag length, in bits.
* @return {bitArray} The encrypted data, an array of bytes. * @return {bitArray} The encrypted data, an array of bytes.
*/ */
encrypt: function(prf, plaintext, iv, adata, tlen) { encrypt: function (prf, plaintext, iv, adata, tlen) {
var L, var L,
out = plaintext.slice(0), out = plaintext.slice(0),
tag, tag,
@ -747,7 +750,7 @@ sjcl.mode.ccm = {
* @param {Number} [tlen=64] tlen the desired tag length, in bits. * @param {Number} [tlen=64] tlen the desired tag length, in bits.
* @return {bitArray} The decrypted data. * @return {bitArray} The decrypted data.
*/ */
decrypt: function(prf, ciphertext, iv, adata, tlen) { decrypt: function (prf, ciphertext, iv, adata, tlen) {
tlen = tlen || 64; tlen = tlen || 64;
adata = adata || []; adata = adata || [];
var L, var L,
@ -783,7 +786,7 @@ sjcl.mode.ccm = {
return out.data; return out.data;
}, },
_macAdditionalData: function(prf, adata, iv, tlen, ol, L) { _macAdditionalData: function (prf, adata, iv, tlen, ol, L) {
var mac, var mac,
tmp, tmp,
i, i,
@ -827,7 +830,7 @@ sjcl.mode.ccm = {
* @return {bitArray} The tag, but not yet encrypted. * @return {bitArray} The tag, but not yet encrypted.
* @private * @private
*/ */
_computeTag: function(prf, plaintext, iv, adata, tlen, L) { _computeTag: function (prf, plaintext, iv, adata, tlen, L) {
// compute B[0] // compute B[0]
var mac, var mac,
i, i,
@ -868,7 +871,7 @@ sjcl.mode.ccm = {
* @return {Object} An object with data and tag, the en/decryption of data and tag values. * @return {Object} An object with data and tag, the en/decryption of data and tag values.
* @private * @private
*/ */
_ctrMode: function(prf, data, iv, tag, tlen, L) { _ctrMode: function (prf, data, iv, tag, tlen, L) {
var enc, var enc,
i, i,
w = sjcl.bitArray, w = sjcl.bitArray,
@ -906,7 +909,7 @@ sjcl.mode.ccm = {
data[i + 3] ^= enc[3]; data[i + 3] ^= enc[3];
} }
return { tag: tag, data: w.clamp(data, bl) }; return { tag: tag, data: w.clamp(data, bl) };
} },
}; };
export { sjcl }; export { sjcl };

View File

@ -1,43 +1,43 @@
{ {
"name": "@padloc/electron", "name": "@padloc/electron",
"version": "4.0.0", "version": "4.0.0",
"description": "Electron Wrapper for Padloc app", "description": "Electron Wrapper for Padloc app",
"main": "main.js", "main": "main.js",
"private": true, "private": true,
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/padloc/padloc.git" "url": "https://github.com/padloc/padloc.git"
}, },
"engines": { "engines": {
"node": "16.13.1", "node": "16.13.1",
"npm": "8.2.0" "npm": "8.2.0"
}, },
"scripts": { "scripts": {
"build": "rm -rf app && webpack && node ./prepare-build.js && electron-builder --config build/build.json", "build": "rm -rf app && webpack && node ./prepare-build.js && electron-builder --config build/build.json",
"start": "webpack && electron app/main.js" "start": "webpack && electron app/main.js"
}, },
"author": "MaKleSoft UG", "author": "MaKleSoft UG",
"license": "GPL-3.0", "license": "GPL-3.0",
"dependencies": { "dependencies": {
"@padloc/app": "4.0.0", "@padloc/app": "4.0.0",
"@padloc/core": "4.0.0", "@padloc/core": "4.0.0",
"electron-store": "8.0.0", "electron-store": "8.0.0",
"electron-updater": "4.3.9" "electron-updater": "4.3.9"
}, },
"devDependencies": { "devDependencies": {
"clean-webpack-plugin": "3.0.0", "clean-webpack-plugin": "3.0.0",
"css-loader": "5.2.6", "css-loader": "5.2.6",
"electron": "14.1.1", "electron": "14.1.1",
"electron-builder": "22.13.1", "electron-builder": "22.13.1",
"electron-notarize": "1.1.1", "electron-notarize": "1.1.1",
"file-loader": "6.2.0", "file-loader": "6.2.0",
"html-webpack-plugin": "5.3.2", "html-webpack-plugin": "5.3.2",
"style-loader": "2.0.0", "style-loader": "2.0.0",
"ts-loader": "9.2.5", "ts-loader": "9.2.5",
"webpack": "5.52.1", "webpack": "5.52.1",
"webpack-cli": "4.8.0", "webpack-cli": "4.8.0",
"typescript": "4.4.3", "typescript": "4.4.3",
"raw-loader": "4.0.2", "raw-loader": "4.0.2",
"sharp": "0.29.1" "sharp": "0.29.1"
} }
} }

View File

@ -3,7 +3,7 @@ const { execSync } = require("child_process");
const { tmpdir } = require("os"); const { tmpdir } = require("os");
const { writeFileSync, unlinkSync } = require("fs"); const { writeFileSync, unlinkSync } = require("fs");
exports.default = async function(opts) { exports.default = async function (opts) {
const jsign = path.resolve(__dirname, "jsign-2.1.jar"); const jsign = path.resolve(__dirname, "jsign-2.1.jar");
const pass = process.env.PL_WIN_SIGN_PASS; const pass = process.env.PL_WIN_SIGN_PASS;
const lib = path.resolve(__dirname, "crypto3PKCS.dylib"); const lib = path.resolve(__dirname, "crypto3PKCS.dylib");
@ -18,9 +18,7 @@ slotListIndex=0`
); );
execSync( execSync(
`java -jar ${jsign} --keystore ${keystore} --storepass ${pass} --storetype PKCS11 -t http://time.certum.pl/ -a "${ `java -jar ${jsign} --keystore ${keystore} --storepass ${pass} --storetype PKCS11 -t http://time.certum.pl/ -a "${opts.options.publisherName}" "${opts.path}"`
opts.options.publisherName
}" "${opts.path}"`
); );
unlinkSync(keystore); unlinkSync(keystore);

View File

@ -1,3 +1,3 @@
{ {
"extends": "../app/tsconfig.json" "extends": "../app/tsconfig.json"
} }

View File

@ -1,47 +1,47 @@
{ {
"name": "@padloc/extension", "name": "@padloc/extension",
"version": "4.0.0", "version": "4.0.0",
"description": "Padloc Progressive Web App", "description": "Padloc Progressive Web App",
"author": "Martin Kleinschrodt <martin@maklesoft.com>", "author": "Martin Kleinschrodt <martin@maklesoft.com>",
"license": "GPL-3.0", "license": "GPL-3.0",
"private": true, "private": true,
"files": [ "files": [
"src", "src",
"assets", "assets",
"tsconfig.json" "tsconfig.json"
], ],
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/padloc/padloc.git", "url": "https://github.com/padloc/padloc.git",
"directory": "packages/pwa" "directory": "packages/pwa"
}, },
"engines": { "engines": {
"node": "16.13.1", "node": "16.13.1",
"npm": "8.2.0" "npm": "8.2.0"
}, },
"dependencies": { "dependencies": {
"@padloc/app": "4.0.0", "@padloc/app": "4.0.0",
"@padloc/core": "4.0.0", "@padloc/core": "4.0.0",
"@webcomponents/webcomponentsjs": "2.5.0", "@webcomponents/webcomponentsjs": "2.5.0",
"webextension-polyfill-ts": "0.25.0" "webextension-polyfill-ts": "0.25.0"
}, },
"devDependencies": { "devDependencies": {
"clean-webpack-plugin": "3.0.0", "clean-webpack-plugin": "3.0.0",
"css-loader": "5.2.6", "css-loader": "5.2.6",
"favicons-webpack-plugin": "5.0.2", "favicons-webpack-plugin": "5.0.2",
"file-loader": "6.2.0", "file-loader": "6.2.0",
"html-webpack-plugin": "5.3.1", "html-webpack-plugin": "5.3.1",
"http-server": "0.12.3", "http-server": "0.12.3",
"raw-loader": "4.0.2", "raw-loader": "4.0.2",
"sharp": "0.29.1", "sharp": "0.29.1",
"style-loader": "2.0.0", "style-loader": "2.0.0",
"ts-loader": "9.2.2", "ts-loader": "9.2.2",
"ts-node": "10.0.0", "ts-node": "10.0.0",
"typescript": "4.4.3", "typescript": "4.4.3",
"webpack": "5.38.1", "webpack": "5.38.1",
"webpack-cli": "4.7.0" "webpack-cli": "4.7.0"
}, },
"scripts": { "scripts": {
"build": "webpack" "build": "webpack"
} }
} }

View File

@ -183,19 +183,19 @@ class ExtensionContent {
input.dispatchEvent( input.dispatchEvent(
new KeyboardEvent("keydown", { new KeyboardEvent("keydown", {
bubbles: true, bubbles: true,
key: "" key: "",
}) })
); );
input.dispatchEvent( input.dispatchEvent(
new KeyboardEvent("keyup", { new KeyboardEvent("keyup", {
bubbles: true, bubbles: true,
key: "" key: "",
}) })
); );
input.dispatchEvent( input.dispatchEvent(
new KeyboardEvent("keypress", { new KeyboardEvent("keypress", {
bubbles: true, bubbles: true,
key: "" key: "",
}) })
); );
input.dispatchEvent(new Event("input", { bubbles: true })); input.dispatchEvent(new Event("input", { bubbles: true }));

View File

@ -4,22 +4,14 @@
"128": "icon.png" "128": "icon.png"
}, },
"background": { "background": {
"scripts": [ "scripts": ["background.js"],
"background.js"
],
"persistent": true "persistent": true
}, },
"browser_action": { "browser_action": {
"default_popup": "popup.html", "default_popup": "popup.html",
"default_icon": "icon.png" "default_icon": "icon.png"
}, },
"permissions": [ "permissions": ["storage", "unlimitedStorage", "tabs", "activeTab", "contextMenus"],
"storage",
"unlimitedStorage",
"tabs",
"activeTab",
"contextMenus"
],
"commands": { "commands": {
"_execute_browser_action": { "_execute_browser_action": {
"suggested_key": { "suggested_key": {
@ -42,4 +34,4 @@
"description": "Open Previous Vault Item" "description": "Open Previous Vault Item"
} }
} }
} }

View File

@ -1,28 +1,28 @@
{ {
"name": "@padloc/locale", "name": "@padloc/locale",
"version": "4.0.0", "version": "4.0.0",
"description": "Package containing translations and wordlists for Padloc as well as various other localization tools", "description": "Package containing translations and wordlists for Padloc as well as various other localization tools",
"main": "src/index.js", "main": "src/index.js",
"files": [ "files": [
"src", "src",
"res" "res"
], ],
"scripts": { "scripts": {
"extract": "ts-node src/extract.ts ../*/src/{**,.}/*.ts" "extract": "ts-node src/extract.ts ../*/src/{**,.}/*.ts"
}, },
"author": "Martin Kleinschrodt <martin@maklesoft.com>", "author": "Martin Kleinschrodt <martin@maklesoft.com>",
"license": "GPLv3", "license": "GPLv3",
"devDependencies": { "devDependencies": {
"ts-node": "10.0.0", "ts-node": "10.0.0",
"typescript": "4.4.3" "typescript": "4.4.3"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/padloc/padloc.git", "url": "https://github.com/padloc/padloc.git",
"directory": "packages/locale" "directory": "packages/locale"
}, },
"engines": { "engines": {
"node": "16.13.1", "node": "16.13.1",
"npm": "8.2.0" "npm": "8.2.0"
} }
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -235,5 +235,5 @@ export const countries = [
{ code: "EH", name: "Western Sahara" }, { code: "EH", name: "Western Sahara" },
{ code: "YE", name: "Yemen" }, { code: "YE", name: "Yemen" },
{ code: "ZM", name: "Zambia" }, { code: "ZM", name: "Zambia" },
{ code: "ZW", name: "Zimbabwe" } { code: "ZW", name: "Zimbabwe" },
]; ];

View File

@ -54,7 +54,7 @@ function extract(files: ts.SourceFile[]) {
items.set(original, { items.set(original, {
original, original,
translation: "", translation: "",
sources: [] sources: [],
}); });
} }
@ -63,7 +63,7 @@ function extract(files: ts.SourceFile[]) {
const source: ItemSource = { const source: ItemSource = {
file: file.fileName, file: file.fileName,
line, line,
character character,
}; };
const comments = const comments =
@ -106,7 +106,7 @@ export function toYAML({ language, date, commit, items }: Translation) {
commit: ${commit} commit: ${commit}
`; `;
doc.contents = YAML.createNode(items.flatMap(item => [item.original, item.translation])) as any; doc.contents = YAML.createNode(items.flatMap((item) => [item.original, item.translation])) as any;
for (const [i, item] of items.entries()) { for (const [i, item] of items.entries()) {
const node = (doc.contents as any).items[i * 2]; const node = (doc.contents as any).items[i * 2];
@ -131,7 +131,7 @@ export function fromYAML(str: string, language: string): Translation {
language, language,
date: new Date(), date: new Date(),
commit: "", commit: "",
items items,
}; };
} }
@ -156,37 +156,35 @@ export function fromJSON(str: string, language: string) {
const items = JSON.parse(str).map(([original, translation]: [string, string]) => ({ const items = JSON.parse(str).map(([original, translation]: [string, string]) => ({
original, original,
translation, translation,
sources: [] sources: [],
})); }));
return { return {
language, language,
date: new Date(), date: new Date(),
commit: "", commit: "",
items items,
}; };
} }
export function fromSource(fileNames: string[], language: string): Translation { export function fromSource(fileNames: string[], language: string): Translation {
const files = fileNames.map(path => const files = fileNames.map((path) =>
ts.createSourceFile(path, readFileSync(resolve(path), "utf8"), ts.ScriptTarget.ES2017, false) ts.createSourceFile(path, readFileSync(resolve(path), "utf8"), ts.ScriptTarget.ES2017, false)
); );
const commit = execSync("git rev-parse HEAD") const commit = execSync("git rev-parse HEAD").toString().trim();
.toString()
.trim();
const items = extract(files); const items = extract(files);
return { return {
language, language,
commit, commit,
date: new Date(), date: new Date(),
items items,
}; };
} }
export function merge(curr: Translation, prev: Translation) { export function merge(curr: Translation, prev: Translation) {
for (const item of curr.items) { for (const item of curr.items) {
const prevItemIndex = prev.items.findIndex(i => i.original === item.original); const prevItemIndex = prev.items.findIndex((i) => i.original === item.original);
if (prevItemIndex !== -1) { if (prevItemIndex !== -1) {
item.translation = prev.items[prevItemIndex].translation; item.translation = prev.items[prevItemIndex].translation;
prev.items.splice(prevItemIndex, 1); prev.items.splice(prevItemIndex, 1);

View File

@ -2,9 +2,7 @@
"extends": "../../tsconfig.json", "extends": "../../tsconfig.json",
"compilerOptions": { "compilerOptions": {
"target": "es2017", "target": "es2017",
"lib": [ "lib": ["esnext"],
"esnext"
],
"module": "commonjs", "module": "commonjs",
"declaration": false, "declaration": false,
"baseUrl": "." "baseUrl": "."

View File

@ -1,51 +1,51 @@
{ {
"name": "@padloc/pwa", "name": "@padloc/pwa",
"version": "4.0.0", "version": "4.0.0",
"author": "Martin Kleinschrodt <martin@maklesoft.com>", "author": "Martin Kleinschrodt <martin@maklesoft.com>",
"license": "GPL-3.0", "license": "GPL-3.0",
"private": true, "private": true,
"files": [ "files": [
"src", "src",
"assets", "assets",
"tsconfig.json" "tsconfig.json"
], ],
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/padloc/padloc.git", "url": "https://github.com/padloc/padloc.git",
"directory": "packages/pwa" "directory": "packages/pwa"
}, },
"engines": { "engines": {
"node": "16.13.1", "node": "16.13.1",
"npm": "8.2.0" "npm": "8.2.0"
}, },
"dependencies": { "dependencies": {
"@padloc/app": "4.0.0", "@padloc/app": "4.0.0",
"@padloc/core": "4.0.0" "@padloc/core": "4.0.0"
}, },
"devDependencies": { "devDependencies": {
"clean-webpack-plugin": "3.0.0", "clean-webpack-plugin": "3.0.0",
"css-loader": "5.2.6", "css-loader": "5.2.6",
"file-loader": "6.2.0", "file-loader": "6.2.0",
"html-webpack-plugin": "5.3.1", "html-webpack-plugin": "5.3.1",
"http-server": "0.12.3", "http-server": "0.12.3",
"raw-loader": "4.0.2", "raw-loader": "4.0.2",
"sharp": "0.29.3", "sharp": "0.29.3",
"style-loader": "2.0.0", "style-loader": "2.0.0",
"ts-loader": "9.2.2", "ts-loader": "9.2.2",
"ts-node": "10.0.0", "ts-node": "10.0.0",
"typescript": "4.4.3", "typescript": "4.4.3",
"webpack": "5.52.0", "webpack": "5.52.0",
"webpack-cli": "4.8.0", "webpack-cli": "4.8.0",
"webpack-dev-server": "4.2.1", "webpack-dev-server": "4.2.1",
"webpack-pwa-manifest": "4.3.0", "webpack-pwa-manifest": "4.3.0",
"workbox-cli": "6.2.4", "workbox-cli": "6.2.4",
"workbox-webpack-plugin": "6.4.2" "workbox-webpack-plugin": "6.4.2"
}, },
"description": "Padloc Progressive Web App", "description": "Padloc Progressive Web App",
"scripts": { "scripts": {
"build": "webpack", "build": "webpack",
"dev": "webpack serve", "dev": "webpack serve",
"start": "http-server ${PL_PWA_DIR:-dist} -s -p ${PL_PWA_PORT:-8080} --proxy ${PL_PWA_URL:-http://0.0.0.0:${PL_PWA_PORT:-8080}}?", "start": "http-server ${PL_PWA_DIR:-dist} -s -p ${PL_PWA_PORT:-8080} --proxy ${PL_PWA_URL:-http://0.0.0.0:${PL_PWA_PORT:-8080}}?",
"build_and_start": "npm run build && npm start" "build_and_start": "npm run build && npm start"
} }
} }

View File

@ -1,63 +1,63 @@
{ {
"name": "@padloc/server", "name": "@padloc/server",
"version": "3.1.3", "version": "3.1.3",
"description": "Padloc server component", "description": "Padloc server component",
"private": true, "private": true,
"files": [ "files": [
"src", "src",
"tsconfig.json" "tsconfig.json"
], ],
"main": "index.js", "main": "index.js",
"author": "Martin Kleinschrodt <martin@maklesoft.com>", "author": "Martin Kleinschrodt <martin@maklesoft.com>",
"license": "GPLv3", "license": "GPLv3",
"devDependencies": { "devDependencies": {
"@types/chai": "4.2.18", "@types/chai": "4.2.18",
"@types/mocha": "8.2.2", "@types/mocha": "8.2.2",
"chai": "4.3.4", "chai": "4.3.4",
"mocha": "8.4.0", "mocha": "8.4.0",
"ts-node-dev": "1.1.6" "ts-node-dev": "1.1.6"
}, },
"dependencies": { "dependencies": {
"@padloc/core": "4.0.0", "@padloc/core": "4.0.0",
"@padloc/locale": "4.0.0", "@padloc/locale": "4.0.0",
"@aws-sdk/client-s3": "3.25.0", "@aws-sdk/client-s3": "3.25.0",
"@aws-sdk/types": "3.25.0", "@aws-sdk/types": "3.25.0",
"@simplewebauthn/server": "4.0.0", "@simplewebauthn/server": "4.0.0",
"@types/fs-extra": "9.0.11", "@types/fs-extra": "9.0.11",
"@types/mixpanel": "2.14.3", "@types/mixpanel": "2.14.3",
"@types/node": "15.6.1", "@types/node": "15.6.1",
"@types/nodemailer": "6.4.2", "@types/nodemailer": "6.4.2",
"@types/pg": "8.6.1", "@types/pg": "8.6.1",
"@types/stripe": "8.0.416", "@types/stripe": "8.0.416",
"ansi-colors": "4.1.1", "ansi-colors": "4.1.1",
"date-fns": "2.22.1", "date-fns": "2.22.1",
"fs-extra": "10.0.0", "fs-extra": "10.0.0",
"geolite2-redist": "2.0.4", "geolite2-redist": "2.0.4",
"level": "7.0.0", "level": "7.0.0",
"maxmind": "4.3.2", "maxmind": "4.3.2",
"mixpanel": "0.13.0", "mixpanel": "0.13.0",
"mongodb": "4.1.0", "mongodb": "4.1.0",
"nodemailer": "6.6.1", "nodemailer": "6.6.1",
"pg": "8.7.1", "pg": "8.7.1",
"stripe": "8.194.0", "stripe": "8.194.0",
"ts-node": "10.0.0", "ts-node": "10.0.0",
"typescript": "4.4.3" "typescript": "4.4.3"
}, },
"scripts": { "scripts": {
"start": "ts-node src/init.ts", "start": "ts-node src/init.ts",
"start-dry": "ts-node src/init.ts --dryrun", "start-dry": "ts-node src/init.ts --dryrun",
"repl": "ts-node src/init-repl-client.ts", "repl": "ts-node src/init-repl-client.ts",
"dev": "ts-node-dev src/init.ts", "dev": "ts-node-dev src/init.ts",
"dev-inspect": "node -r ts-node/register --inspect-brk --stack-trace-limit=1000 src/init.ts", "dev-inspect": "node -r ts-node/register --inspect-brk --stack-trace-limit=1000 src/init.ts",
"test": "cd test && mocha -r ts-node/register *.ts --timeout 5000" "test": "cd test && mocha -r ts-node/register *.ts --timeout 5000"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/padloc/padloc.git", "url": "https://github.com/padloc/padloc.git",
"directory": "packages/server" "directory": "packages/server"
}, },
"engines": { "engines": {
"node": "16.13.1", "node": "16.13.1",
"npm": "8.2.0" "npm": "8.2.0"
} }
} }

View File

@ -12,7 +12,7 @@ import {
createCipheriv, createCipheriv,
createDecipheriv, createDecipheriv,
publicEncrypt, publicEncrypt,
privateDecrypt privateDecrypt,
} from "crypto"; } from "crypto";
import { import {
CryptoProvider, CryptoProvider,
@ -29,7 +29,7 @@ import {
RSAEncryptionParams, RSAEncryptionParams,
HashParams, HashParams,
RSASigningParams, RSASigningParams,
PBKDF2Params PBKDF2Params,
} from "@padloc/core/src/crypto"; } from "@padloc/core/src/crypto";
import { Err, ErrorCode } from "@padloc/core/src/error"; import { Err, ErrorCode } from "@padloc/core/src/error";
import { equalCT } from "@padloc/core/src/encoding"; import { equalCT } from "@padloc/core/src/encoding";
@ -80,12 +80,12 @@ export class NodeCryptoProvider implements CryptoProvider {
), ),
publicKeyEncoding: { publicKeyEncoding: {
type: "spki", type: "spki",
format: "der" format: "der",
}, },
privateKeyEncoding: { privateKeyEncoding: {
type: "pkcs8", type: "pkcs8",
format: "der" format: "der",
} },
} as any, } as any,
(err, publicKey: Buffer, privateKey: Buffer) => { (err, publicKey: Buffer, privateKey: Buffer) => {
if (err) { if (err) {
@ -93,7 +93,7 @@ export class NodeCryptoProvider implements CryptoProvider {
} else { } else {
resolve({ resolve({
privateKey: new Uint8Array(privateKey), privateKey: new Uint8Array(privateKey),
publicKey: new Uint8Array(publicKey) publicKey: new Uint8Array(publicKey),
}); });
} }
} }
@ -205,7 +205,7 @@ export class NodeCryptoProvider implements CryptoProvider {
const [alg, mode] = params.algorithm.toLowerCase().split("-"); const [alg, mode] = params.algorithm.toLowerCase().split("-");
const authTagLength = params.tagSize / 8; const authTagLength = params.tagSize / 8;
const cipher = createCipheriv(`${alg}-${params.keySize}-${mode}` as "aes-256-gcm", key, params.iv, { const cipher = createCipheriv(`${alg}-${params.keySize}-${mode}` as "aes-256-gcm", key, params.iv, {
authTagLength authTagLength,
} as any); } as any);
cipher.setAAD(params.additionalData as Buffer); cipher.setAAD(params.additionalData as Buffer);
try { try {
@ -223,7 +223,7 @@ export class NodeCryptoProvider implements CryptoProvider {
const tag = data.slice(tagPos); const tag = data.slice(tagPos);
const cipher = createDecipheriv(`${alg}-${params.keySize}-${mode}` as "aes-256-gcm", key, params.iv, { const cipher = createDecipheriv(`${alg}-${params.keySize}-${mode}` as "aes-256-gcm", key, params.iv, {
authTagLength authTagLength,
} as any); } as any);
cipher.setAAD(params.additionalData as Buffer); cipher.setAAD(params.additionalData as Buffer);
cipher.setAuthTag(tag); cipher.setAuthTag(tag);
@ -242,7 +242,7 @@ export class NodeCryptoProvider implements CryptoProvider {
key: Buffer.from(publicKey), key: Buffer.from(publicKey),
format: "der", format: "der",
type: "spki", type: "spki",
oaepHash: hashToNode(params.hash) oaepHash: hashToNode(params.hash),
} as any, } as any,
key key
); );
@ -259,7 +259,7 @@ export class NodeCryptoProvider implements CryptoProvider {
key: Buffer.from(privateKey), key: Buffer.from(privateKey),
format: "der", format: "der",
type: "pkcs8", type: "pkcs8",
oaepHash: hashToNode(params.hash) oaepHash: hashToNode(params.hash),
} as any, } as any,
key key
); );
@ -293,7 +293,7 @@ export class NodeCryptoProvider implements CryptoProvider {
format: "der", format: "der",
dsaEncoding: "der", dsaEncoding: "der",
saltLength: params.saltLength, saltLength: params.saltLength,
padding: constants.RSA_PKCS1_PSS_PADDING padding: constants.RSA_PKCS1_PSS_PADDING,
}); });
return new Uint8Array(sig); return new Uint8Array(sig);
@ -309,7 +309,7 @@ export class NodeCryptoProvider implements CryptoProvider {
format: "der", format: "der",
dsaEncoding: "der", dsaEncoding: "der",
saltLength: params.saltLength, saltLength: params.saltLength,
padding: constants.RSA_PKCS1_PSS_PADDING padding: constants.RSA_PKCS1_PSS_PADDING,
}, },
signature signature
); );

View File

@ -21,10 +21,10 @@ export class NodeLegacyServer implements LegacyServer {
method: "GET", method: "GET",
headers: { headers: {
Authorization: `SkeletonKey ${email}:${this.config.key}`, Authorization: `SkeletonKey ${email}:${this.config.key}`,
Accept: "application/vnd.padlock;version=1" Accept: "application/vnd.padlock;version=1",
} },
}, },
res => { (res) => {
if (res.statusCode !== 200) { if (res.statusCode !== 200) {
resolve(null); resolve(null);
return; return;
@ -33,7 +33,7 @@ export class NodeLegacyServer implements LegacyServer {
let data = ""; let data = "";
res.setEncoding("utf8"); res.setEncoding("utf8");
res.on("data", chunk => (data += chunk)); res.on("data", (chunk) => (data += chunk));
res.on("end", () => { res.on("end", () => {
if (!data) { if (!data) {
resolve(null); resolve(null);
@ -65,10 +65,10 @@ export class NodeLegacyServer implements LegacyServer {
method: "POST", method: "POST",
headers: { headers: {
Authorization: `SkeletonKey ${email}:${this.config.key}`, Authorization: `SkeletonKey ${email}:${this.config.key}`,
Accept: "application/vnd.padlock;version=1" Accept: "application/vnd.padlock;version=1",
} },
}, },
res => { (res) => {
if (res.statusCode !== 200) { if (res.statusCode !== 200) {
reject("Received status code " + res.statusCode); reject("Received status code " + res.statusCode);
return; return;

View File

@ -34,7 +34,8 @@ export class DefaultAccountProvisioning
Pick< Pick<
AccountProvisioning, AccountProvisioning,
"status" | "statusLabel" | "statusMessage" | "actionUrl" | "actionLabel" | "quota" | "disableFeatures" "status" | "statusLabel" | "statusMessage" | "actionUrl" | "actionLabel" | "quota" | "disableFeatures"
> { >
{
@ConfigParam() @ConfigParam()
status: ProvisioningStatus = ProvisioningStatus.Active; status: ProvisioningStatus = ProvisioningStatus.Active;
@ -400,16 +401,8 @@ export class SimpleProvisioner implements Provisioner {
} }
} }
const { const { accountId, status, statusLabel, statusMessage, actionUrl, actionLabel, scheduledUpdates, metaData } =
accountId, entry.toRaw();
status,
statusLabel,
statusMessage,
actionUrl,
actionLabel,
scheduledUpdates,
metaData,
} = entry.toRaw();
httpRes.statusCode = 200; httpRes.statusCode = 200;
httpRes.end( httpRes.end(

View File

@ -1,36 +1,36 @@
{ {
"name": "@padloc/tauri", "name": "@padloc/tauri",
"version": "4.0.0", "version": "4.0.0",
"description": "Tauri Wrapper for packaging native apps", "description": "Tauri Wrapper for packaging native apps",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"tauri": "tauri", "tauri": "tauri",
"build": "tauri build", "build": "tauri build",
"dev": "tauri dev" "dev": "tauri dev"
}, },
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"devDependencies": { "devDependencies": {
"clean-webpack-plugin": "3.0.0", "clean-webpack-plugin": "3.0.0",
"css-loader": "5.2.6", "css-loader": "5.2.6",
"file-loader": "6.2.0", "file-loader": "6.2.0",
"html-webpack-plugin": "5.3.1", "html-webpack-plugin": "5.3.1",
"style-loader": "2.0.0", "style-loader": "2.0.0",
"ts-loader": "9.2.2", "ts-loader": "9.2.2",
"ts-node": "10.0.0", "ts-node": "10.0.0",
"typescript": "4.3.2", "typescript": "4.3.2",
"webpack": "5.38.1", "webpack": "5.38.1",
"webpack-cli": "4.7.0", "webpack-cli": "4.7.0",
"webpack-dev-server": "3.11.2", "webpack-dev-server": "3.11.2",
"@tauri-apps/cli": "1.0.0-beta.10" "@tauri-apps/cli": "1.0.0-beta.10"
}, },
"dependencies": { "dependencies": {
"@padloc/app": "4.0.0", "@padloc/app": "4.0.0",
"@padloc/core": "4.0.0", "@padloc/core": "4.0.0",
"@tauri-apps/api": "1.0.0-beta.8" "@tauri-apps/api": "1.0.0-beta.8"
}, },
"engines": { "engines": {
"node": "16.13.1", "node": "16.13.1",
"npm": "8.2.0" "npm": "8.2.0"
} }
} }

View File

@ -1,65 +1,61 @@
{ {
"package": { "package": {
"productName": "Padloc", "productName": "Padloc",
"version": "0.1.0" "version": "0.1.0"
},
"build": {
"distDir": "../dist",
"devPath": "http://localhost:8080",
"beforeDevCommand": "webpack serve",
"beforeBuildCommand": "webpack"
},
"tauri": {
"bundle": {
"active": true,
"targets": "all",
"identifier": "app.padloc",
"icon": [
"icons/icon.png",
"icons/icon.icns",
"icons/icon.ico"
],
"resources": [],
"externalBin": [],
"copyright": "",
"category": "DeveloperTool",
"shortDescription": "",
"longDescription": "",
"deb": {
"depends": [],
"useBootstrapper": false
},
"macOS": {
"frameworks": [],
"minimumSystemVersion": "",
"useBootstrapper": false,
"exceptionDomain": "",
"signingIdentity": null,
"entitlements": null
},
"windows": {
"certificateThumbprint": null,
"digestAlgorithm": "sha256",
"timestampUrl": ""
}
}, },
"updater": { "build": {
"active": false "distDir": "../dist",
"devPath": "http://localhost:8080",
"beforeDevCommand": "webpack serve",
"beforeBuildCommand": "webpack"
}, },
"allowlist": { "tauri": {
"all": true "bundle": {
}, "active": true,
"windows": [ "targets": "all",
{ "identifier": "app.padloc",
"title": "Padloc", "icon": ["icons/icon.png", "icons/icon.icns", "icons/icon.ico"],
"width": 1024, "resources": [],
"height": 768, "externalBin": [],
"resizable": true, "copyright": "",
"fullscreen": false "category": "DeveloperTool",
} "shortDescription": "",
], "longDescription": "",
"security": { "deb": {
"csp": "default-src blob: data: filesystem: ws: http: https: 'unsafe-eval' 'unsafe-inline' 'self' img-src: 'self'" "depends": [],
"useBootstrapper": false
},
"macOS": {
"frameworks": [],
"minimumSystemVersion": "",
"useBootstrapper": false,
"exceptionDomain": "",
"signingIdentity": null,
"entitlements": null
},
"windows": {
"certificateThumbprint": null,
"digestAlgorithm": "sha256",
"timestampUrl": ""
}
},
"updater": {
"active": false
},
"allowlist": {
"all": true
},
"windows": [
{
"title": "Padloc",
"width": 1024,
"height": 768,
"resizable": true,
"fullscreen": false
}
],
"security": {
"csp": "default-src blob: data: filesystem: ws: http: https: 'unsafe-eval' 'unsafe-inline' 'self' img-src: 'self'"
}
} }
} }
}

View File

@ -58,7 +58,6 @@
rotate linear 1.2s infinite; rotate linear 1.2s infinite;
} }
</style> </style>
</head> </head>
<body> <body>
<svg viewBox="0 0 100 100" class="spinner"> <svg viewBox="0 0 100 100" class="spinner">

View File

@ -14,7 +14,6 @@ function createApp() {
await import("@padloc/app/src/elements/app"); await import("@padloc/app/src/elements/app");
if (document.readyState === "loading") { if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", createApp); document.addEventListener("DOMContentLoaded", createApp);
} else { } else {