From c5124731f5f2a9ef4aacb6b2e1949c2c4ca78039 Mon Sep 17 00:00:00 2001 From: Knut Ahlers Date: Mon, 2 Oct 2023 21:52:24 +0200 Subject: [PATCH] [#115] Implement Binary File Attachments (#116) --- README.md | 16 ++++++ cli_create.sh | 2 +- cli_get.sh | 2 +- customize.go | 22 +++++--- i18n.yaml | 13 +++++ package-lock.json | 2 +- package.json | 1 + src/components/create.vue | 88 +++++++++++++++++++++++++++++-- src/components/secret-display.vue | 48 +++++++++++++++-- src/crypto.js | 52 +++++++++++++++--- src/helpers.js | 21 ++++++++ src/langs/langs.js | 6 +-- src/ots-meta.js | 88 +++++++++++++++++++++++++++++++ 13 files changed, 330 insertions(+), 31 deletions(-) create mode 100644 src/helpers.js create mode 100644 src/ots-meta.js diff --git a/README.md b/README.md index c51cd24..13ab1dc 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,22 @@ overlayFSPath: /path/to/ots-customization # Languages not having a formal version will still display the normal # translations in the respective language. useFormalLanguage: false + +# Define which file types are selectable by the user when uploading +# files to attach. This fuels the `accept` attribute of the file +# select and requires the same format. Pay attention this is not +# suited as a security measure as this is purely a frontend +# implementation and can be circumvented. +# https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#accept +acceptedFileTypes: '' + +# Disable the file attachment functionality alltogether +disableFileAttachment: false + +# Define how big all attachments might be in bytes. Leave it set to +# zero to use the internal limit of 64 MiB (which is there to ensure +# the encrypted object does not cause the frontend to break). +maxAttachmentSizeTotal: 0 ``` To override the styling of the application have a look at the [`src/style.scss`](./src/style.scss) file how the theme of the application is built and present the compiled `app.css` in the `overlayFSPath`. diff --git a/cli_create.sh b/cli_create.sh index 69f9bce..736e471 100644 --- a/cli_create.sh +++ b/cli_create.sh @@ -22,7 +22,7 @@ SECRET=${1:-} pass=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | head -c 20 || true) # Encrypt the secret -ciphertext=$(echo "${SECRET}" | openssl aes-256-cbc -base64 -pass "pass:${pass}" -iter 300000 -md sha512 2>/dev/null) +ciphertext=$(echo "${SECRET}" | openssl aes-256-cbc -base64 -A -pass "pass:${pass}" -iter 300000 -md sha512 2>/dev/null) # Create a secret and extract the secret ID id=$( diff --git a/cli_get.sh b/cli_get.sh index 03ce712..96208fc 100644 --- a/cli_get.sh +++ b/cli_get.sh @@ -25,4 +25,4 @@ geturl="${host}/api/get/${id}" # fetch secret and decrypt to STDOUT curl -sSf "${geturl}" | jq -r ".secret" | - openssl aes-256-cbc -base64 -pass "pass:${pass}" -iter 300000 -md sha512 -d 2>/dev/null + openssl aes-256-cbc -base64 -A -pass "pass:${pass}" -iter 300000 -md sha512 -d diff --git a/customize.go b/customize.go index 4797fe5..bf808c5 100644 --- a/customize.go +++ b/customize.go @@ -12,16 +12,22 @@ import ( type ( customize struct { - AppIcon string `json:"appIcon,omitempty" yaml:"appIcon"` - AppTitle string `json:"appTitle,omitempty" yaml:"appTitle"` - DisableAppTitle bool `json:"disableAppTitle,omitempty" yaml:"disableAppTitle"` + AppIcon string `json:"appIcon,omitempty" yaml:"appIcon"` + AppTitle string `json:"appTitle,omitempty" yaml:"appTitle"` + DisableAppTitle bool `json:"disableAppTitle,omitempty" yaml:"disableAppTitle"` + DisablePoweredBy bool `json:"disablePoweredBy,omitempty" yaml:"disablePoweredBy"` + DisableQRSupport bool `json:"disableQRSupport,omitempty" yaml:"disableQRSupport"` + DisableThemeSwitcher bool `json:"disableThemeSwitcher,omitempty" yaml:"disableThemeSwitcher"` + DisableExpiryOverride bool `json:"disableExpiryOverride,omitempty" yaml:"disableExpiryOverride"` - DisablePoweredBy bool `json:"disablePoweredBy,omitempty" yaml:"disablePoweredBy"` - DisableQRSupport bool `json:"disableQRSupport,omitempty" yaml:"disableQRSupport"` - DisableThemeSwitcher bool `json:"disableThemeSwitcher,omitempty" yaml:"disableThemeSwitcher"` ExpiryChoices []int64 `json:"expiryChoices,omitempty" yaml:"expiryChoices"` - OverlayFSPath string `json:"-" yaml:"overlayFSPath"` - UseFormalLanguage bool `json:"-" yaml:"useFormalLanguage"` + + AcceptedFileTypes string `json:"acceptedFileTypes" yaml:"acceptedFileTypes"` + DisableFileAttachment bool `json:"disableFileAttachment" yaml:"disableFileAttachment"` + MaxAttachmentSizeTotal int64 `json:"maxAttachmentSizeTotal" yaml:"maxAttachmentSizeTotal"` + + OverlayFSPath string `json:"-" yaml:"overlayFSPath"` + UseFormalLanguage bool `json:"-" yaml:"useFormalLanguage"` } ) diff --git a/i18n.yaml b/i18n.yaml index c104f34..472e783 100644 --- a/i18n.yaml +++ b/i18n.yaml @@ -5,8 +5,10 @@ reference: alert-secret-not-found: This is not the secret you are looking for… - If you expected the secret to be here it might be compromised as someone else might have opened the link already. alert-something-went-wrong: Something went wrong. I'm very sorry about this… btn-create-secret: Create the secret! + btn-create-secret-processing: Secret is being created… btn-new-secret: New Secret btn-reveal-secret: Show me the secret! + btn-reveal-secret-processing: Secret is being decrypted… btn-show-explanation: How does this work? expire-default: Default Expiry expire-n-days: '{n} day | {n} days' @@ -23,9 +25,13 @@ reference: - After the encrypted secret has been retrieved once, it is deleted from the server label-expiry: 'Expire in:' label-secret-data: 'Secret data:' + label-secret-files: 'Attach Files:' + text-attached-files: The sender attached files to the secret. Make sure you trust the sender as the files were not checked! text-burn-hint: Please remember not to go to this URL yourself as that would destroy the secret. Just pass it to someone else! text-burn-time: 'If not viewed before, this secret will automatically be deleted:' text-hint-burned: Attention: You're only seeing this once. As soon as you reload the page the secret will be gone so maybe copy it now… + text-max-filesize: 'Maximum size: {maxSize}' + text-max-filesize-exceeded: 'The file(s) you chose are too big to attach: {curSize} / {maxSize}' text-powered-by: Powered by text-pre-reveal-hint: To reveal the secret click this button but be aware doing so will destroy the secret. You can only view it once! text-pre-url: 'Your secret was created and stored using this URL:' @@ -77,8 +83,10 @@ translations: alert-secret-not-found: Das ist nicht das Secret, was du suchst… - Falls du diesen Link noch nicht selbst geöffnet hast, könnte das Secret kompromittiert sein, da jemand anderes den Link geöffnet haben könnte. alert-something-went-wrong: Irgendwas ging schief. Entschuldigung… btn-create-secret: Secret erstellen! + btn-create-secret-processing: Secret wird erstellt… btn-new-secret: Neues Secret btn-reveal-secret: Zeig mir das Secret! + btn-reveal-secret-processing: Secret wird entschlüsselt… btn-show-explanation: Wie funktioniert das? expire-default: Server-Standard expire-n-days: '{n} Tag | {n} Tage' @@ -95,9 +103,13 @@ translations: - Wenn das verschlüsselte Secret das erste Mal abgerufen wurde, wird es automatisch vom Server gelöscht label-expiry: 'Ablauf in:' label-secret-data: 'Inhalt des Secrets:' + label-secret-files: 'Dateien Anhängen:' + text-attached-files: Der Absender hat Dateien an das Secret angehängt. Stell sicher, dass du dem Absender vertraust, da die Dateien nicht geprüft wurden! text-burn-hint: Bitte rufe die URL nicht selbst auf, da das Secret dadurch zerstört würde. Gib sie einfach weiter! text-burn-time: 'Wenn es vorher nicht eingesehen wurde, wird dieses Secret automatisch gelöscht:' text-hint-burned: Achtung: Du kannst das nur einmal ansehen! Sobald du die Seite neu lädst, ist das Secret verschwunden, also besser direkt kopieren und sicher abspeichern… + text-max-filesize: 'Maximale Größe: {maxSize}' + text-max-filesize-exceeded: 'Die ausgewählten Dateien übersteigen die maximale Größe: {curSize} / {maxSize}' text-powered-by: Läuft mit text-pre-reveal-hint: Um das Secret anzuzeigen klicke diesen Button aber denk dran, dass das Secret nur einmal angezeigt und dabei gelöscht wird. text-pre-url: 'Dein Secret wurde angelegt und unter folgender URL gespeichert:' @@ -118,6 +130,7 @@ translations: - Sie geben die angezeigte URL, welche die ID und das Passwort des Secrets enthält, an den Empfänger - 'Der Empfänger kann das Secret einmalig abrufen: Funktioniert das nicht, könnte jemand anderes es abgerufen haben!' - Wenn das verschlüsselte Secret das erste Mal abgerufen wurde, wird es automatisch vom Server gelöscht + text-attached-files: Der Absender hat Dateien an das Secret angehängt. Stellen Sie sicher, dass Sie dem Absender vertrauen, da die Dateien nicht geprüft wurden! text-burn-hint: Bitte rufen Sie die URL nicht selbst auf, da das Secret dadurch zerstört würde. text-hint-burned: Achtung: Sie können das Secret nur einmal ansehen! Sobald Sie die Seite neu laden, kann das Secret nicht erneut abgerufen werden, also besser direkt kopieren und sicher abspeichern… text-pre-reveal-hint: Klicken Sie auf diesen Button um das Secret anzuzeigen, bedenken Sie aber, dass das Secret nur einmal angezeigt und dabei gelöscht wird. diff --git a/package-lock.json b/package-lock.json index 60fb383..79c3138 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,6 +6,7 @@ "": { "name": "ots", "dependencies": { + "base64-js": "^1.5.1", "bootstrap": "^5.3.2", "qrcode": "^1.5.3", "vue": "^2.7.14", @@ -1166,7 +1167,6 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, "funding": [ { "type": "github", diff --git a/package.json b/package.json index 32385da..da55311 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "name": "ots", "private": true, "dependencies": { + "base64-js": "^1.5.1", "bootstrap": "^5.3.2", "qrcode": "^1.5.3", "vue": "^2.7.14", diff --git a/src/components/create.vue b/src/components/create.vue index 2f1e321..111ce75 100644 --- a/src/components/create.vue +++ b/src/components/create.vue @@ -29,7 +29,7 @@ class="row" @submit.prevent="createSecret" > -
+