Add notepad

This commit is contained in:
proteriax 2021-10-26 20:48:58 -04:00
parent 7c12f499f2
commit 26f8485761
14 changed files with 2590 additions and 934 deletions

3
.gitignore vendored
View File

@ -4,4 +4,5 @@ lib
docs
ref
*.opvault
freddy
freddy
electron/bundled

View File

@ -9,4 +9,4 @@
".vscode-insiders",
"i18n.json"
]
}
}

280
Untitled.ipynb Normal file
View File

@ -0,0 +1,280 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 2,
"id": "aa8a7fc4",
"metadata": {},
"outputs": [],
"source": [
"import fs from \"fs\";\n",
"import { resolve } from \"path\";"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "d909c03d",
"metadata": {},
"outputs": [],
"source": [
"import { OnePassword } from \"./src/index\";"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "56b32a8e-af25-4356-8e13-ba8ca840f986",
"metadata": {},
"outputs": [],
"source": [
"const instance = new OnePassword({ path: \"./freddy-2013-12-04.opvault\" });\n",
"const vault = await instance.getProfile(\"default\");\n",
"await vault.unlock(\"freddy\");\n",
"void 0;"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "6c940edd-e225-4397-a92f-4fa8927854ab",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[\n",
" { ps: 0 },\n",
" {\n",
" title: 'Personal',\n",
" ainfo: 'Wendy Appleseed',\n",
" tags: [ 'Sample', 'Personal' ],\n",
" ps: 0\n",
" },\n",
" {\n",
" title: 'Hulu',\n",
" URLs: [ [Object] ],\n",
" ainfo: 'wendy@appleseed.com',\n",
" url: 'http://www.hulu.com/',\n",
" tags: [ 'Sample' ],\n",
" ps: 66\n",
" },\n",
" {\n",
" title: \"Wendy's driver's license\",\n",
" ps: 0,\n",
" ainfo: 'D6101-40706-60905'\n",
" },\n",
" { title: 'Orders', ainfo: '10.0.1.50', tags: [ 'Sample' ], ps: 0 },\n",
" {\n",
" URLs: [ [Object] ],\n",
" tags: [ 'Sample' ],\n",
" title: 'Skype',\n",
" url: 'https://secure.skype.com/account/login?message=login_required',\n",
" ainfo: 'WendyAppleseed',\n",
" ps: 78\n",
" },\n",
" {\n",
" title: 'YouTube',\n",
" URLs: [ [Object] ],\n",
" ainfo: 'wendy@appleseed.com',\n",
" url: 'http://www.youtube.com/login?next=/index',\n",
" tags: [ 'Sample' ],\n",
" ps: 78\n",
" },\n",
" { title: 'example.com', ps: 0, ainfo: 'wappleseed' },\n",
" {\n",
" title: 'Dropbox',\n",
" URLs: [ [Object] ],\n",
" ainfo: 'wendy@appleseed.com',\n",
" url: 'https://www.getdropbox.com/',\n",
" tags: [ 'Sample' ],\n",
" ps: 78\n",
" },\n",
" {\n",
" title: \"Company's FTP\",\n",
" URLs: [ [Object] ],\n",
" ainfo: 'admin',\n",
" url: 'ftp://ftp.dreamhost.com',\n",
" tags: [ 'Sample' ],\n",
" ps: 60\n",
" },\n",
" {\n",
" title: 'Tumblr',\n",
" URLs: [ [Object] ],\n",
" ainfo: 'wendy@appleseed.com',\n",
" url: 'http://www.tumblr.com/login',\n",
" tags: [ 'Sample' ],\n",
" ps: 48\n",
" },\n",
" { title: 'Social Security', ps: 0, ainfo: 'Wendy Appleseed' },\n",
" {\n",
" title: 'Last.fm',\n",
" URLs: [ [Object] ],\n",
" ainfo: 'WendyAppleseed',\n",
" url: 'https://www.last.fm/login',\n",
" tags: [ 'Sample' ],\n",
" ps: 72\n",
" },\n",
" { title: 'Tim Hortons', ps: 0, ainfo: 'Tim Hortens' },\n",
" { title: 'Snipe Hunting License', ps: 0, ainfo: 'Wendy Appleseed' },\n",
" {\n",
" title: 'A note to Trash',\n",
" ainfo: 'Lets create a note that we will throw in the trash but not expunge.',\n",
" ps: 0\n",
" },\n",
" {\n",
" title: 'CapitalOne MasterCard ***3456',\n",
" ainfo: '1234 *********** 3456',\n",
" tags: [ 'Sample' ],\n",
" ps: 0\n",
" },\n",
" {\n",
" title: 'What is a Secure Note?',\n",
" ainfo: '',\n",
" tags: [ 'Sample' ],\n",
" ps: 0\n",
" },\n",
" {\n",
" title: 'The Unofficial Apple Weblog',\n",
" URLs: [ [Object] ],\n",
" ainfo: 'WendyAppleseed',\n",
" url: 'http://www.tuaw.com',\n",
" tags: [ 'Sample' ],\n",
" ps: 78\n",
" },\n",
" { title: \"Wendy's passport\", ps: 0, ainfo: 'ZZ200000' },\n",
" {\n",
" title: 'Chase VISA ***4356',\n",
" ainfo: '1234 *********** 4356',\n",
" tags: [ 'Sample' ],\n",
" ps: 0\n",
" },\n",
" {\n",
" title: 'Bank of America',\n",
" URLs: [ [Object] ],\n",
" ainfo: 'WendyAppleseed',\n",
" url: 'https://www.bankofamerica.com/',\n",
" tags: [ 'Sample', 'Personal' ],\n",
" ps: 66\n",
" },\n",
" {\n",
" title: 'A note with some attachments',\n",
" ps: 0,\n",
" ainfo: 'This note has two attachments.'\n",
" },\n",
" { title: '1Password', ainfo: '3.0', tags: [ 'Sample' ], ps: 0 },\n",
" { title: 'TextExpander', ainfo: '1.3', tags: [ 'Sample' ], ps: 0 },\n",
" {\n",
" title: 'Business',\n",
" ainfo: 'Wendy Appleseed',\n",
" tags: [ 'Business', 'Sample' ],\n",
" ps: 0\n",
" },\n",
" {\n",
" title: 'MobileMe',\n",
" URLs: [ [Object] ],\n",
" ainfo: 'wendy.appleseed@me.com',\n",
" url: 'https://www.icloud.com/',\n",
" tags: [ 'Sample' ],\n",
" ps: 66\n",
" },\n",
" { title: 'Email Account', ps: 0, ainfo: 'wendy.appleseed@me.com' },\n",
" {\n",
" title: 'Johnny Appleseed Society',\n",
" ps: 0,\n",
" ainfo: 'Wendy Appleseed'\n",
" }\n",
"]"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"vault.overviews.values()"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "1547939f-3ee1-49df-9401-c54b8511acb4",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[Object: null prototype] {\n",
" async: [Function (anonymous)],\n",
" done: [Function (anonymous)],\n",
" sendResult: [Function (anonymous)],\n",
" sendError: [Function (anonymous)],\n",
" mime: [Function (anonymous)],\n",
" text: [Function (anonymous)],\n",
" html: [Function (anonymous)],\n",
" svg: [Function (anonymous)],\n",
" png: [Function (anonymous)],\n",
" jpeg: [Function (anonymous)],\n",
" json: [Function (anonymous)],\n",
" input: [Function (anonymous)],\n",
" display: [Function (anonymous)],\n",
" clear: [Function (anonymous)]\n",
"}"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"$$"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "3625d249-b27e-4990-b433-94238a60f3db",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<a href=\"#\">meow</a>"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"$$.jsx(<a href=\"#\">meow</a>)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9899de99-b1a9-4f91-9d48-a63808baedc1",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "jp-Babel (Node.js)",
"language": "babel",
"name": "babel"
},
"language_info": {
"file_extension": ".js",
"mimetype": "application/javascript",
"name": "javascript",
"version": "16.10.0"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

8
electron/app/index.tsx Normal file
View File

@ -0,0 +1,8 @@
const replaceText = (selector, text) => {
const element = document.getElementById(selector)
if (element) element.innerText = text
}
for (const type of ["chrome", "node", "electron"]) {
replaceText(`${type}-version`, process.versions[type])
}

22
electron/index.html Normal file
View File

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'"
/>
<meta
http-equiv="X-Content-Security-Policy"
content="default-src 'self'; script-src 'self'"
/>
<title>Hello World!</title>
</head>
<body>
<h1>Hello World!</h1>
We are using Node.js <span id="node-version"></span>, Chromium
<span id="chrome-version"></span>, and Electron <span id="electron-version"></span>.
<script src="bundled/index.js"></script>
</body>
</html>

46
electron/index.js Normal file
View File

@ -0,0 +1,46 @@
// @ts-check
// Modules to control application life and create native browser window
// const path = require("path")
const { app, BrowserWindow } = require("electron")
function createWindow() {
// Create the browser window.
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
// preload: path.join(__dirname, "preload.js"),
},
})
// and load the index.html of the app.
mainWindow.loadFile("index.html")
// Open the DevTools.
// mainWindow.webContents.openDevTools()
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(() => {
createWindow()
app.on("activate", () => {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on("window-all-closed", () => {
if (process.platform !== "darwin") app.quit()
})
// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.

44
esbuild.js Executable file
View File

@ -0,0 +1,44 @@
#!/usr/bin/env node
// @ts-check
const { build } = require("esbuild")
const sassPlugin = require("esbuild-plugin-sass")
const { nodeBuiltIns } = require("esbuild-node-builtins")
const args = process.argv.slice(2)
build({
bundle: true,
define: {
"process.browser": "true",
"process.env.BLUEPRINT_NAMESPACE": '"bp4"',
global: "globalThis",
},
entryPoints: ["electron/app/index.tsx"],
inject: ["./scripts/react-shim.js"],
outdir: "electron/bundled",
external: ["path", "glob", "fs", "util"],
jsxFactory: "esbuildCreateElement",
jsxFragment: "esbuildFragment",
plugins: [
sassPlugin(),
nodeBuiltIns({
include: ["path", "fs"],
}),
],
target: ["chrome90"],
tsconfig: "./tsconfig.json",
sourcemap: "inline",
minify: process.env.NODE_ENV === "production",
banner: {
js: "/* eslint-disable */",
},
loader: {
".png": "file",
".eot": "file",
".svg": "file",
".woff": "file",
".woff2": "file",
".ttf": "file",
},
watch: args.includes("-w") || args.includes("--watch"),
})

View File

@ -8,44 +8,54 @@
"build": "rollup -c; cp src/adapters/index.d.ts lib/adapters/; prettier --write lib >/dev/null",
"build:docs": "typedoc --out docs src/index.ts --excludePrivate",
"test": "node --expose-gc node_modules/mocha/bin/_mocha test/**/*.test.ts",
"repl": "node -r ts-node/register/transpile-only src/repl.ts"
"repl": "node -r ts-node/register/transpile-only src/repl.ts",
"start": "electron ./electron/index.js"
},
"devDependencies": {
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-replace": "^3.0.0",
"@types/chai": "^4.2.19",
"@types/chai": "^4.2.22",
"@types/chai-as-promised": "^7.1.4",
"@types/fs-extra": "^9.0.11",
"@types/fs-extra": "^9.0.13",
"@types/mocha": "github:whitecolor/mocha-types#da22474cf43f48a56c86f8c23a5a0ea36e295768",
"@types/node": "^16.0.0",
"@types/prompts": "^2.0.13",
"@types/sinon": "^10.0.2",
"@types/node": "^16.10.3",
"@types/prompts": "^2.0.14",
"@types/react": "^17.0.30",
"@types/react-dom": "^17.0.9",
"@types/sinon": "^10.0.4",
"@types/sinon-chai": "^3.2.5",
"@typescript-eslint/eslint-plugin": "4.28.2",
"@typescript-eslint/parser": "4.28.2",
"@typescript-eslint/eslint-plugin": "4.33.0",
"@typescript-eslint/parser": "4.33.0",
"chai": "^4.3.4",
"chai-as-promised": "^7.1.1",
"chalk": "^4.1.1",
"eslint": "7.30.0",
"chalk": "^4.1.2",
"electron": "^15.2.0",
"esbuild": "^0.13.6",
"esbuild-node-builtins": "^0.1.0",
"esbuild-plugin-sass": "^0.6.0",
"eslint": "7.32.0",
"eslint-config-prettier": "8.3.0",
"eslint-import-resolver-typescript": "2.4.0",
"eslint-plugin-import": "2.23.4",
"eslint-plugin-react": "7.24.0",
"eslint-import-resolver-typescript": "2.5.0",
"eslint-plugin-import": "2.24.2",
"eslint-plugin-react": "7.26.1",
"eslint-plugin-react-hooks": "4.2.0",
"fs-extra": "^10.0.0",
"memfs": "^3.2.2",
"mocha": "^9.0.2",
"mochawesome": "^6.2.2",
"prettier": "^2.3.2",
"memfs": "^3.3.0",
"mocha": "^9.1.2",
"mochawesome": "^6.3.0",
"prettier": "^2.4.1",
"prompts": "^2.4.1",
"rollup": "^2.52.7",
"rollup-plugin-ts": "^1.4.0",
"sinon": "^11.1.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"rollup": "^2.58.0",
"rollup-plugin-ts": "^1.4.7",
"sass": "^1.43.2",
"sinon": "^11.1.2",
"sinon-chai": "^3.7.0",
"ts-node": "^10.0.0",
"tsconfig-paths": "^3.9.0",
"typedoc": "^0.21.2",
"typescript": "^4.3.5"
"ts-node": "^10.2.1",
"tsconfig-paths": "^3.11.0",
"typedoc": "^0.22.5",
"typescript": "^4.4.3"
},
"prettier": {
"arrowParens": "avoid",
@ -57,6 +67,6 @@
},
"dependencies": {
"tiny-invariant": "1.1.0",
"tslib": "2.3.0"
"tslib": "2.3.1"
}
}

3002
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

4
scripts/react-shim.js vendored Normal file
View File

@ -0,0 +1,4 @@
import { createElement, Fragment } from "react"
export const esbuildCreateElement = createElement
export const esbuildFragment = Fragment

View File

@ -1,4 +1,5 @@
import { Crypto, decryptOPData } from "./crypto"
import type { Crypto } from "./crypto"
import { decryptOPData } from "./crypto"
import { invariant } from "../errors"
type integer = number

View File

@ -3,13 +3,9 @@ import { createHmac, createDecipheriv, createHash } from "crypto"
import { EventEmitter } from "../ee"
import { HMACAssertionError } from "../errors"
import type { i18n } from "../i18n"
import { ItemDetails, Overview, Profile } from "../types"
import type { ItemDetails, Overview, Profile } from "../types"
import { setIfAbsent } from "../util"
import { EncryptedItem } from "./item"
declare module "crypto" {
export const webcrypto: Crypto
}
import type { EncryptedItem } from "./item"
/** Encryption and MAC */
export interface Cipher {
@ -32,11 +28,11 @@ export class Crypto extends EventEmitter<{ lock: void }> implements IDisposable
unlock(profile: Profile, masterPassword: string) {
const derivedKey = crypto.pbkdf2Sync(
/* password */ masterPassword,
/* salt */ Buffer.from(profile.salt, "base64"),
/* iterations */ profile.iterations,
/* keylen */ 64,
/* digest */ "sha512"
masterPassword,
Buffer.from(profile.salt, "base64"),
profile.iterations,
64,
"sha512"
)
const cipher = splitPlainText(derivedKey)
@ -115,9 +111,7 @@ export class Crypto extends EventEmitter<{ lock: void }> implements IDisposable
}
)
deriveGeneralKey = (input: Buffer) => {
crypto.createHash("sha512").update()
}
deriveGeneralKey = (input: Buffer) => {}
get ov() {
return this.#overview

View File

@ -109,7 +109,7 @@ export class Vault extends EventEmitter<VaultEvents> {
* @param masterPassword User provided master password. Only the derived
* master and overview key will be stored within the class.
*/
unlock(masterPassword: string) {
async unlock(masterPassword: string) {
try {
this.#crypto.unlock(this.#profile, masterPassword)
} catch (e) {

View File

@ -26,24 +26,40 @@ describe("OnePassword", () => {
vault = await new OnePassword({ path: freddy }).getProfile("default");
});
it("accepts correct password", () => {
expect(() => vault.unlock("freddy")).to.not.throw();
it("accepts correct password", async () => {
await expect(vault.unlock("freddy")).to.be.fulfilled;
expect(vault.isLocked).to.be.false;
});
it("rejects wrong password", () => {
["Freddy", "_freddy", ""].forEach((password) => {
expect(() => vault.unlock(password)).to.throw("Invalid password");
["Freddy", "_freddy", ""].forEach(async (password) => {
await expect(vault.unlock(password)).to.be.rejectedWith(
"Invalid password"
);
expect(vault.isLocked).to.be.true;
});
});
});
describe("content", () => {
let vault: Vault;
beforeEach(async () => {
vault = await new OnePassword({ path: freddy }).getProfile("default");
await vault.unlock("freddy");
});
it("reads overviews", () => {
const overviews = vault.overviews.values();
expect(overviews).to.have.lengthOf(29);
});
});
describe("lock", () => {
it("locks", async () => {
const instance = new OnePassword({ path: freddy });
const vault = await instance.getProfile("default");
vault.unlock("freddy");
await vault.unlock("freddy");
expect(vault.isLocked).to.be.false;
vault.lock();