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

@ -12,6 +12,8 @@ jobs:
node-version: 16.13.1
- name: Install dependencies
run: npm ci
- name: Run prettier check
run: npm run prettier:check
- name: Run pwa test build
run: npm run pwa:build
- name: Test starting zero-config server

View File

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

View File

@ -1,7 +1,5 @@
{
"lit-plugin.globalAttributes": [
"disabled"
],
"lit-plugin.globalAttributes": ["disabled"],
"files.associations": {
"*.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]`.
Use `npm run prettier` to make "prettify" all files.
## Configuration
| Environment Variable | Default | Description |

View File

@ -1,4 +1,4 @@
module.exports = function(config) {
module.exports = function (config) {
config.set({
frameworks: ["mocha", "chai"],
files: ["test/tests.js"],
@ -9,6 +9,6 @@ module.exports = function(config) {
browsers: ["ChromeHeadless"],
autoWatch: false,
// 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
}

19
package-lock.json generated
View File

@ -11,6 +11,7 @@
"license": "GPL-3.0",
"devDependencies": {
"lerna": "4.0.0",
"prettier": "2.5.1",
"ts-node": "10.0.0",
"typescript": "4.4.3"
},
@ -5658,6 +5659,18 @@
"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": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
@ -12266,6 +12279,12 @@
"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": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",

View File

@ -5,10 +5,10 @@
"description": "A minimalist password manager",
"author": "Martin Kleinschrodt <martin@maklesoft.com>",
"license": "GPL-3.0",
"homepage": "https://padlock.io/",
"homepage": "https://padloc.app/",
"repository": {
"type": "git",
"url": "git+https://github.com/maklesoft/padlock.git"
"url": "git+https://github.com/padloc/padloc.git"
},
"engines": {
"node": "16.13.1",
@ -17,6 +17,7 @@
"main": "main.js",
"devDependencies": {
"lerna": "4.0.0",
"prettier": "2.5.1",
"ts-node": "10.0.0",
"typescript": "4.4.3"
},
@ -35,6 +36,8 @@
"test": "lerna run test",
"locale:extract": "lerna run extract --scope '@padloc/locale'",
"add": "lerna add $1 --scope=@padloc/$scope",
"remove": "rm packages/$scope/package-lock.json && lerna exec \"npm uninstall $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

@ -45,7 +45,7 @@ export class Dialog<I, R> extends LitElement {
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);
this.open = true;

View File

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

View File

@ -173,9 +173,8 @@ export class Popover extends LitElement {
}
private _getAutoAlignment(): PopoverAlignment {
const preferred = (Array.isArray(this.preferAlignment)
? [...this.preferAlignment]
: [this.preferAlignment]
const preferred = (
Array.isArray(this.preferAlignment) ? [...this.preferAlignment] : [this.preferAlignment]
).reverse();
const alignments = [...ALIGNMENTS].sort((a, b) => preferred.indexOf(b) - preferred.indexOf(a));
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>
</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,11 +1,11 @@
import { loadAsync } from 'jszip';
import { loadAsync } from "jszip";
export type OnePuxItemDetailsLoginField = {
value: string;
id: string;
name: string;
fieldType: 'A' | 'B' | 'C' | 'E' | 'I' | 'N' | 'P' | 'R' | 'S' | 'T' | 'U';
designation?: 'username' | 'password';
fieldType: "A" | "B" | "C" | "E" | "I" | "N" | "P" | "R" | "S" | "T" | "U";
designation?: "username" | "password";
};
export type OnePuxItemDetailsSection = {
@ -38,7 +38,7 @@ export type OnePuxItemDetailsSection = {
correction: string;
capitalization: string;
};
},
}
];
};
@ -98,7 +98,7 @@ export type OnePuxVault = {
desc: string;
avatar: string;
name: string;
type: 'P' | 'E' | 'U';
type: "P" | "E" | "U";
};
items: OnePuxItem[];
};
@ -130,17 +130,13 @@ export type OnePuxExport = {
data: OnePuxData;
};
export const parse1PuxFile = async (
fileContents: string | ArrayBuffer,
) => {
export async function parse1PuxFile(fileContents: ArrayBuffer): Promise<OnePuxExport> {
try {
const zip = await loadAsync(fileContents);
const attributesContent = await zip
.file('export.attributes')!
.async('string');
const attributesContent = await zip.file("export.attributes")!.async("string");
const attributes = JSON.parse(attributesContent);
const dataContent = await zip.file('export.data')!.async('string');
const dataContent = await zip.file("export.data")!.async("string");
const data = JSON.parse(dataContent);
return {
@ -148,10 +144,10 @@ export const parse1PuxFile = async (
data,
} as OnePuxExport;
} catch (error) {
console.error('Failed to parse .1pux file');
console.error("Failed to parse .1pux file");
throw error;
}
};
}
type RowData = {
name: string;
@ -164,75 +160,63 @@ type RowData = {
};
type ExtraFieldType =
| 'username'
| 'password'
| 'url'
| 'email'
| 'date'
| 'month'
| 'credit'
| 'phone'
| 'totp'
| 'text';
| "username"
| "password"
| "url"
| "email"
| "date"
| "month"
| "credit"
| "phone"
| "totp"
| "text";
type ExtraField = { name: string; value: string; type: ExtraFieldType };
type ParseFieldTypeToExtraFieldType = (
field: OnePuxItemDetailsLoginField,
) => ExtraFieldType;
const parseFieldTypeToExtraFieldType: ParseFieldTypeToExtraFieldType = (
field,
) => {
if (field.designation === 'username') {
return 'username';
} else if (field.designation === 'password') {
return 'password';
} else if (field.fieldType === 'E') {
return 'email';
} else if (field.fieldType === 'U') {
return 'url';
function parseFieldTypeToExtraFieldType(field: OnePuxItemDetailsLoginField): ExtraFieldType {
if (field.designation === "username") {
return "username";
} else if (field.designation === "password") {
return "password";
} else if (field.fieldType === "E") {
return "email";
} else if (field.fieldType === "U") {
return "url";
}
return 'text';
};
return "text";
}
export const parseToRowData = (
item: OnePuxItem['item'],
defaultTags?: string[],
) => {
export function parseToRowData(item: OnePuxItem["item"], defaultTags?: string[]): RowData | undefined {
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 || '',
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
) {
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') {
if (field.designation === "username") {
rowData.username = field.value;
} else if (field.designation === 'password') {
} else if (field.designation === "password") {
rowData.password = field.value;
} else if (
field.fieldType === 'I' ||
field.fieldType === 'C' ||
field.id.includes(';opid=__') ||
field.value === ''
field.fieldType === "I" ||
field.fieldType === "C" ||
field.id.includes(";opid=__") ||
field.value === ""
) {
// Skip these noisy form-fields
return;
@ -248,49 +232,40 @@ export const parseToRowData = (
// Extract some more extraFields
item.details.sections.forEach((section) => {
section.fields.forEach((field) => {
let value = '';
let type: ExtraFieldType = 'text';
let value = "";
let type: ExtraFieldType = "text";
if (Object.prototype.hasOwnProperty.call(field.value, 'concealed')) {
value = field.value.concealed || '';
} else if (
Object.prototype.hasOwnProperty.call(field.value, 'reference')
) {
value = field.value.reference || '';
} else if (Object.prototype.hasOwnProperty.call(field.value, 'string')) {
value = field.value.string || '';
} 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';
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);
@ -305,4 +280,4 @@ export const parseToRowData = (
});
return rowData;
};
}

View File

@ -7,7 +7,7 @@ const defaults = {
initialDelay: 0,
fullDuration: 1000,
clear: false,
direction: "normal"
direction: "normal",
};
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}`;
if (clear) {
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 = {}) {

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 { uuid } from "@padloc/core/src/util";
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 {
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;
}
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 parsed = papa.parse(data);
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);
}
/**
* Checks if a given string represents a Padlock enrypted backup
*/
export function isPadlockV1(data: string): boolean {
export async function isPadlockV1(file: File): Promise<boolean> {
try {
const data = await readFileAsText(file);
return validateLegacyContainer(unmarshal(data));
} catch (e) {
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));
await container.unlock(password);
return importLegacyContainer(container);
@ -146,16 +147,18 @@ export async function importLegacyContainer(container: PBES2Container) {
return Promise.all(items);
}
export function isPBES2Container(data: string) {
export async function isPBES2Container(file: File) {
try {
const data = await readFileAsText(file);
new PBES2Container().fromRaw(unmarshal(data));
return true;
} catch (e) {
} catch (error) {
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));
await container.unlock(password);
@ -234,7 +237,8 @@ async function lpParseRow(row: string[]): Promise<VaultItem> {
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();
let items = papa
.parse(data)
@ -247,22 +251,26 @@ export async function asLastPass(data: string): Promise<VaultItem[]> {
return Promise.all(items);
}
/**
* Checks if a given string represents a LastPass CSV file
*/
export function isLastPass(data: string): boolean {
export async function isLastPass(file: File): Promise<boolean> {
try {
const data = await readFileAsText(file);
return data.split("\n")[0] === "url,username,password,extra,name,grouping,fav";
} catch (error) {
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) {
return;
}
const { parseToRowData } = await import("./1pux-parser");
const rowData = parseToRowData(item, [accountName, vaultName]);
if (!rowData) {
@ -270,10 +278,10 @@ async function parse1PuxItem(accountName: string, vaultName: string, item: OnePu
}
const itemName = rowData.name;
const tags = rowData.tags.split(',');
const tags = rowData.tags.split(",");
if (item.trashed) {
tags.push('trashed');
tags.push("trashed");
}
let fields: Field[] = [
@ -287,10 +295,10 @@ async function parse1PuxItem(accountName: string, vaultName: string, item: OnePu
}
for (const extraField of rowData.extraFields) {
if (extraField.type === 'totp') {
if (extraField.type === "totp") {
// Extract just the secret
try {
const secret = new URL(extraField.value).searchParams.get('secret');
const secret = new URL(extraField.value).searchParams.get("secret");
if (secret) {
fields.push(new Field({ name: extraField.name, value: secret, type: FieldType.Totp }));
}
@ -298,20 +306,19 @@ async function parse1PuxItem(accountName: string, vaultName: string, item: OnePu
// Do nothing
}
} 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);
}
export async function as1Pux(data: null | string | ArrayBuffer): Promise<VaultItem[]> {
if (!data) {
throw new Err(ErrorCode.INVALID_1PUX);
}
export async function as1Pux(file: File): Promise<VaultItem[]> {
try {
const { parse1PuxFile } = await import("./1pux-parser");
const data = await readFileAsArrayBuffer(file);
const dataExport = await parse1PuxFile(data);
const items = [];
@ -331,7 +338,7 @@ export async function as1Pux(data: null | string | ArrayBuffer): Promise<VaultIt
return items;
} 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
*/
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 {
if (isPBES2Container(data as string)) {
return PBES2;
}
if (isPadlockV1(data as string)) {
return PADLOCK_LEGACY;
}
if (isLastPass(data as string)) {
return LASTPASS;
}
export async function guessFormat(file: File): Promise<ImportFormat> {
// Try to guess sync first (won't need parsing)
if (is1Pux(file)) {
return ONEPUX;
}
if (await isPBES2Container(file)) {
return PBES2;
}
if (await isPadlockV1(file)) {
return PADLOCK_LEGACY;
}
if (await isLastPass(file)) {
return LASTPASS;
}
return CSV;
}
export function doesFileRequireReadingAsBinary(file: File): boolean {
if (is1Pux(file)) {
return true;
}
return false;
}

View File

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

View File

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

View File

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

View File

@ -105,24 +105,7 @@ module.exports = {
.toBuffer();
const iosIconSizes = [
20,
29,
40,
50,
57,
58,
60,
72,
76,
80,
87,
100,
114,
120,
144,
152,
167,
180,
20, 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];

View File

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

View File

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

View File

@ -41,10 +41,10 @@ export class Client extends API {
method,
typeof input === "undefined" ? [] : [input instanceof Serializable ? input.toRaw() : input],
progress
).then(res => {
).then((res) => {
return output
? 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)
: res.result;
}) as PromiseWithProgress<any>;
@ -85,7 +85,7 @@ export class Client extends API {
}
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) {
progress.error = err;
}

View File

@ -31,7 +31,7 @@ export class VaultItemCollection extends Serializable implements Iterable<VaultI
constructor(items: VaultItem[] = []) {
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` */
@ -91,8 +91,8 @@ export class VaultItemCollection extends Serializable implements Iterable<VaultI
protected _toRaw(version: string) {
return {
items: Array.from(this).map(item => item.toRaw(version)),
changes: [...this._changes]
items: Array.from(this).map((item) => item.toRaw(version)),
changes: [...this._changes],
};
}

View File

@ -65,15 +65,15 @@ export function parseLegacyContainer(raw: LegacyContainer): PBES2Container {
tagSize: raw.ts,
keySize: raw.keySize,
iv: raw.iv,
additionalData: raw.adata
additionalData: raw.adata,
},
keyParams: {
algorithm: "PBKDF2",
hash: "SHA-256",
keySize: raw.keySize,
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) => ({
mainVault: { id: mainVault },
orgs: orgs.map((org: any) => ({
id: org
id: org,
})),
...rest
...rest,
}),
down: ({ mainVault, orgs, ...rest }: any) => ({
mainVault: mainVault.id,
orgs: orgs.map((org: any) => org.id),
...rest
})
...rest,
}),
},
all: {
up: (raw: any, kind?: string) => ({ kind, ...raw }),
down: ({ kind, ...rest }) => rest
}
}
}
down: ({ kind, ...rest }) => rest,
},
},
},
];
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 {
return version
.split(".")
.map(part => part.padStart(3, "0"))
.map((part) => part.padStart(3, "0"))
.join();
}
@ -58,7 +58,7 @@ export function upgrade(kind: string, raw: any, version: string = LATEST_VERSION
}
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) {
@ -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 {
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) {

View File

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

View File

@ -30,7 +30,7 @@ type Position = [number, number];
export const defaults: Options = {
height: 9,
width: 17,
symbols: [" ", ".", "o", "+", "=", "*", "B", "O", "X", "@", "%", "&", "#", "/", "^", "S", "E"]
symbols: [" ", ".", "o", "+", "=", "*", "B", "O", "X", "@", "%", "&", "#", "/", "^", "S", "E"],
};
// Converts a buffer to a binary moves array
@ -120,6 +120,6 @@ export function randomArt(fingerprint: Uint8Array, opts: Partial<Options> = {}):
width: options.width,
height: options.height,
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) {
throw new Err(ErrorCode.ENCRYPTION_FAILED);
}
}
},
};
export default SJCLProvider;

View File

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

View File

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

View File

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

View File

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

View File

@ -1369,174 +1369,14 @@ function bnModInverse(m) {
}
var lowprimes = [
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,
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
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,
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];

View File

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

View File

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

View File

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

View File

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

View File

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

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: "YE", name: "Yemen" },
{ 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, {
original,
translation: "",
sources: []
sources: [],
});
}
@ -63,7 +63,7 @@ function extract(files: ts.SourceFile[]) {
const source: ItemSource = {
file: file.fileName,
line,
character
character,
};
const comments =
@ -106,7 +106,7 @@ export function toYAML({ language, date, commit, items }: Translation) {
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()) {
const node = (doc.contents as any).items[i * 2];
@ -131,7 +131,7 @@ export function fromYAML(str: string, language: string): Translation {
language,
date: new Date(),
commit: "",
items
items,
};
}
@ -156,37 +156,35 @@ export function fromJSON(str: string, language: string) {
const items = JSON.parse(str).map(([original, translation]: [string, string]) => ({
original,
translation,
sources: []
sources: [],
}));
return {
language,
date: new Date(),
commit: "",
items
items,
};
}
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)
);
const commit = execSync("git rev-parse HEAD")
.toString()
.trim();
const commit = execSync("git rev-parse HEAD").toString().trim();
const items = extract(files);
return {
language,
commit,
date: new Date(),
items
items,
};
}
export function merge(curr: Translation, prev: Translation) {
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) {
item.translation = prev.items[prevItemIndex].translation;
prev.items.splice(prevItemIndex, 1);

View File

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

View File

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

View File

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

View File

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

View File

@ -14,11 +14,7 @@
"active": true,
"targets": "all",
"identifier": "app.padloc",
"icon": [
"icons/icon.png",
"icons/icon.icns",
"icons/icon.ico"
],
"icon": ["icons/icon.png", "icons/icon.icns", "icons/icon.ico"],
"resources": [],
"externalBin": [],
"copyright": "",

View File

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

View File

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