mirror of
https://github.com/cupcakearmy/cryptgeon.git
synced 2025-09-04 08:30:39 +00:00
Compare commits
16 Commits
Author | SHA1 | Date | |
---|---|---|---|
5dff12ea70 | |||
e332dc63e8 | |||
a18e9bcc88 | |||
4b43edf54a | |||
e3aa2dd5ff | |||
98a03c25e6 | |||
7f618e7e45 | |||
84a7be4549 | |||
b2bad5f64c | |||
41f55c0920 | |||
edbf8a8ecf | |||
4852804581 | |||
22b1c35b3e | |||
d1e9ffd89b | |||
9c675ba48c | |||
ef3d3d5bde |
18
.github/workflows/test.yml
vendored
Normal file
18
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
name: test
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
|
||||
jobs:
|
||||
text:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Install
|
||||
run: |
|
||||
docker-compose build
|
||||
npm ci
|
||||
- name: Test
|
||||
run: npm run test:run
|
23
CHANGELOG.md
23
CHANGELOG.md
@@ -5,7 +5,28 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [1.0.9] - 2021-05-08
|
||||
## [1.1.0] - 2021-05-16
|
||||
|
||||
### Security
|
||||
|
||||
- Using hash `#` instead of path
|
||||
|
||||
## [1.0.11] - 2021-05-08
|
||||
|
||||
### Added
|
||||
|
||||
- loading text
|
||||
- description for created notes about availability
|
||||
|
||||
### Changed
|
||||
|
||||
- iterations from 100 to 100k
|
||||
|
||||
### Fixed
|
||||
|
||||
- time based view bug
|
||||
|
||||
## [1.0.10] - 2021-05-08
|
||||
|
||||
### Fixed
|
||||
|
||||
|
@@ -36,7 +36,7 @@ export function getKeyFromString(password: string) {
|
||||
}
|
||||
|
||||
export async function getDerivedForKey(key: CryptoKey, salt: ArrayBuffer) {
|
||||
const iterations = 1_000
|
||||
const iterations = 100_000
|
||||
return window.crypto.subtle.deriveKey(
|
||||
{
|
||||
name: 'PBKDF2',
|
||||
|
@@ -50,7 +50,8 @@
|
||||
password: password,
|
||||
id: response.id,
|
||||
}
|
||||
} catch {
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
error = 'could not create note.'
|
||||
} finally {
|
||||
loading = false
|
||||
@@ -66,26 +67,49 @@
|
||||
<TextInput
|
||||
type="text"
|
||||
readonly
|
||||
value="{window.location.origin}/note/{result.id}/{result.password}"
|
||||
label="share link"
|
||||
value="{window.location.origin}/note/{result.id}#{result.password}"
|
||||
copy
|
||||
data-testid="note-share-link"
|
||||
/>
|
||||
<br />
|
||||
<Button on:click={reset}>new</Button>
|
||||
<p>
|
||||
<b>availability:</b>
|
||||
<br />
|
||||
the note is not guaranteed to be stored as everything is kept in ram, if it fills up the oldest notes
|
||||
will be removed.
|
||||
<br />
|
||||
(you probably will be fine, just be warned.)
|
||||
</p>
|
||||
<br />
|
||||
<Button on:click={reset}>new note</Button>
|
||||
{:else}
|
||||
<form on:submit|preventDefault={submit}>
|
||||
<fieldset disabled={loading}>
|
||||
<TextArea label="note" bind:value={note.contents} placeholder="..." />
|
||||
<TextArea
|
||||
label="note"
|
||||
bind:value={note.contents}
|
||||
placeholder="..."
|
||||
data-testid="input-note"
|
||||
/>
|
||||
|
||||
<div class="bottom">
|
||||
<Switch label="advanced" bind:value={advanced} />
|
||||
<Button type="submit">create</Button>
|
||||
<Button type="submit" data-testid="button-create">create</Button>
|
||||
</div>
|
||||
|
||||
{#if error}
|
||||
<div class="error-text">{error}</div>
|
||||
{/if}
|
||||
|
||||
<p><br />{message}</p>
|
||||
<p>
|
||||
<br />
|
||||
{#if loading}
|
||||
loading...
|
||||
{:else}
|
||||
{message}
|
||||
{/if}
|
||||
</p>
|
||||
|
||||
<div class="advanced" class:hidden={!advanced}>
|
||||
<br />
|
||||
|
@@ -7,18 +7,17 @@
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte'
|
||||
import copy from 'copy-to-clipboard'
|
||||
|
||||
import type { NotePublic } from '$lib/api'
|
||||
import { info, get } from '$lib/api'
|
||||
import { decrypt, getKeyFromString } from '$lib/crypto'
|
||||
import Button from '$lib/ui/Button.svelte'
|
||||
import TextInput from '$lib/ui/TextInput.svelte'
|
||||
import copy from 'copy-to-clipboard'
|
||||
|
||||
import { onMount } from 'svelte'
|
||||
|
||||
export let id: string
|
||||
export let password: string
|
||||
|
||||
let password: string
|
||||
let note: NotePublic | null = null
|
||||
let exists = false
|
||||
|
||||
@@ -29,6 +28,8 @@
|
||||
try {
|
||||
loading = true
|
||||
error = null
|
||||
password = window.location.hash.slice(1)
|
||||
console.log(password)
|
||||
await info(id)
|
||||
exists = true
|
||||
} catch {
|
||||
@@ -41,30 +42,36 @@
|
||||
async function show() {
|
||||
try {
|
||||
error = false
|
||||
loading = true
|
||||
const data = note || (await get(id)) // Don't get the content twice on wrong password.
|
||||
const key = await getKeyFromString(password)
|
||||
data.contents = await decrypt(data.contents, key)
|
||||
note = data
|
||||
} catch {
|
||||
error = true
|
||||
} finally {
|
||||
loading = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if !loading}
|
||||
{#if !exists}
|
||||
<p class="error-text">note was not found or was already deleted.</p>
|
||||
<p class="error-text" data-testid="note-not-found">
|
||||
note was not found or was already deleted.
|
||||
</p>
|
||||
{:else if note && !error}
|
||||
<p class="error-text">you will not get the chance to see the note again.</p>
|
||||
<div class="note">
|
||||
<div class="note" data-testid="note-result">
|
||||
{note.contents}
|
||||
</div>
|
||||
<br />
|
||||
<Button on:click={() => copy(note.contents)}>copy to clipboard</Button>
|
||||
{:else}
|
||||
<form on:submit|preventDefault={show}>
|
||||
<fieldset>
|
||||
<p>click below to show and delete the note if the counter has reached it's limit</p>
|
||||
<Button type="submit">show note</Button>
|
||||
<Button type="submit" data-testid="button-show">show note</Button>
|
||||
{#if error}
|
||||
<br />
|
||||
<p class="error-text">
|
||||
@@ -72,9 +79,13 @@
|
||||
<br />
|
||||
</p>
|
||||
{/if}
|
||||
</fieldset>
|
||||
</form>
|
||||
{/if}
|
||||
{/if}
|
||||
{#if loading}
|
||||
<p>loading...</p>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.note {
|
5
cypress.json
Normal file
5
cypress.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"fixturesFolder": false,
|
||||
"pluginsFile": false,
|
||||
"supportFile": false
|
||||
}
|
2
cypress/.gitignore
vendored
Normal file
2
cypress/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
screenshots
|
||||
videos
|
41
cypress/integration/main.spec.js
Normal file
41
cypress/integration/main.spec.js
Normal file
@@ -0,0 +1,41 @@
|
||||
function createNote(options = {}) {
|
||||
Object.assign(options, {
|
||||
text: `Revaluation battle selfish derive suicide revaluation society love superiority salvation spirit virtues revaluation. Aversion sexuality play burying mountains intentions battle reason strong burying war insofar inexpedient war. Fearful intentions selfish madness suicide.`,
|
||||
})
|
||||
cy.visit('http://localhost:5000')
|
||||
const text = options.text
|
||||
cy.get('[data-testid=input-note]').type(text)
|
||||
cy.get('[data-testid=button-create]').click()
|
||||
cy.wait(500)
|
||||
return cy
|
||||
.get('[data-testid=note-share-link]')
|
||||
.invoke('val')
|
||||
.then((link) => {
|
||||
return [link, text]
|
||||
})
|
||||
}
|
||||
|
||||
describe('Basics', () => {
|
||||
it('Share note', () => {
|
||||
createNote().then(([link, text]) => {
|
||||
cy.visit(link)
|
||||
cy.get('[data-testid=button-show]').click()
|
||||
cy.wait(250)
|
||||
cy.get('[data-testid=note-result]').should('have.text', text)
|
||||
})
|
||||
})
|
||||
|
||||
it('Check destroyed', () => {
|
||||
createNote().then(([link, text]) => {
|
||||
// Check the first time
|
||||
cy.visit(link)
|
||||
cy.get('[data-testid=button-show]').click()
|
||||
cy.wait(250)
|
||||
cy.get('[data-testid=note-result]').should('have.text', text)
|
||||
|
||||
// Should not exists anymore
|
||||
cy.visit(link)
|
||||
cy.get('[data-testid=note-not-found]').should('exist')
|
||||
})
|
||||
})
|
||||
})
|
@@ -15,4 +15,4 @@ services:
|
||||
depends_on:
|
||||
- memcached
|
||||
ports:
|
||||
- 80:5000
|
||||
- 5000:5000
|
||||
|
4572
package-lock.json
generated
4572
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -3,9 +3,14 @@
|
||||
"dev:docker": "docker-compose up memcached",
|
||||
"dev:backend": "cargo watch -x 'run --bin cryptgeon'",
|
||||
"dev:front": "npm --prefix client run dev",
|
||||
"dev": "run-p dev:*"
|
||||
"dev": "run-p dev:*",
|
||||
"test:server": "docker-compose up --build",
|
||||
"test:cypress": "cypress run --headless",
|
||||
"test:run": "start-server-and-test test:server http://localhost:5000 test:cypress"
|
||||
},
|
||||
"devDependencies": {
|
||||
"npm-run-all": "^4.1.5"
|
||||
"cypress": "^7.2.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"start-server-and-test": "^1.12.1"
|
||||
}
|
||||
}
|
||||
|
@@ -56,7 +56,8 @@ async fn create(note: web::Json<Note>) -> impl Responder {
|
||||
if e > 360 {
|
||||
return bad_req;
|
||||
}
|
||||
n.expiration = Some(now() + (e * 60))
|
||||
let expiration = now() + (e * 60);
|
||||
n.expiration = Some(expiration);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -89,8 +90,8 @@ async fn delete(path: web::Path<NotePath>) -> impl Responder {
|
||||
}
|
||||
match changed.expiration {
|
||||
Some(e) => {
|
||||
if e > now() {
|
||||
store::del(&p.id.clone());
|
||||
if e < now() {
|
||||
return HttpResponse::BadRequest().finish();
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user