08 / 15 · Security
Security and possible vulnerabilities
Your files and Markdown notes belong to you. This chapter explains what the app protects, where that protection ends and which risks the builder needs to check.
This description is based on the local source code of 23 October 2026, including the changes in this build package. No penetration test, dependency vulnerability scan or inspection of the active production environment was performed for this chapter. A risk below is therefore not automatically a proven vulnerability. The proposed improvements are not implemented protections yet.
What needs protection?
The browser/Drive-only rule concerns editor notes, titles and merge bases. Separate canvas documents, media annotations, account/folder metadata and legacy local media have explicit server-side storage. They may contain private text: protect and back them up according to Data Structure and the backup matrix. Do not claim all application data lives only in Drive.
- Your account and session: someone else must not be able to impersonate you.
- Your notes and files: others must not be able to read, change or delete them without access.
- Your Google Drive connection: access tokens and refresh tokens must not leak. A refresh token lets the server regain access without requiring you to sign in each time.
- Your data on the device: offline notes, the outgoing queue and browser caches contain private data.
- The service: large uploads, many requests or unsafe files must not take the app down.
Google Drive is the only external file storage. Signing in with Google and granting Drive access are two separate steps. The architecture diagrams show which systems exchange data.
How the current protection works
Sign-in and sessions
The user signs in through Google OAuth. Google checks the Google account. The app validates the returned ID token using the Google library and its own client ID. A random state value links the return from Google to the login attempt that was started. After sign-in, the session receives a new ID so that an old session ID cannot take over the new login.
The cookie mediavault_session contains a session ID. When SESSION_SECRET is configured, this ID is signed with HMAC-SHA-256. This prevents someone from creating a different valid ID. A signature does not encrypt the ID. Session data is stored on the server in sessions.json.
HttpOnlyprevents ordinary page scripts from reading the session cookie.SameSite=Laxlimits when the browser sends the cookie from other sites.Secureis added whenSESSION_COOKIE_SECURE=true. The deployment configuration enables this for HTTPS; the default in the app configuration is off.- By default, a session expires after 90 days of inactivity. Activity extends this period. Logging out removes the current server session.
A stolen valid cookie can still grant access. HttpOnly also does not stop a malicious script from making requests on the user’s behalf. See OWASP on session security.
Access per user
Protected routes check sign-in and whether the account is blocked. The server selects the data folder from the active user; account folders and services are separate. Admin actions have an additional authorization check. Local file IDs are checked for safe paths. Drive upload sessions belong to the session that started the upload.
These are layers of protection. Each endpoint still needs to check that the requested note, file, folder or ticket ID actually belongs to the active user. A hard-to-guess ID alone is not access control.
Google Drive and encryption
The Drive refresh token is stored encrypted with AES-256-GCM. The key is derived from SESSION_SECRET. Access tokens are held temporarily in memory. The server uses them for Drive requests and does not need to store the Google password.
The app server processes note content only temporarily. A pending sync package may be stored temporarily on disk in encrypted form; this package is cleaned up after the result is retrieved. After confirmed sync, the Markdown is in the user’s own Drive. Browser copies remain necessary for offline work.
The app server can read note text during processing and can decrypt stored tokens and sync packages using its key. If both the server and that key are compromised, this protection no longer applies. The code does not add its own end-to-end encryption to Drive files or IndexedDB.
Files, text and network
The code uses HTML escaping and a limited HTML filter for media notes. Upload checks include extension, filename, size, session and received byte count. HTTPS through the reverse proxy protects the browser connection in the documented production setup. Safe sync, revisions and conflict copies reduce accidental overwrites; they do not replace backups or access control.
Possible vulnerabilities and points to check
High means that account access, private data or availability could suffer serious harm. It does not say how easy exploitation currently is. Needs testing means that a risk needs investigation, not that an attack succeeded.
| Risk and impact | What is known? | What should the builder check or improve? |
|---|---|---|
| S01 · Secrets and misconfiguration High · observed in code | Without SESSION_SECRET the app still starts. Sessions are then unsigned. Token and sync encryption have development fallback values. Secure cookies depend on the setting. | Allow production to start only with a strong, unique secret and Secure cookies. Keep keys out of Git and logs. Restrict file permissions and plan key rotation, including reconnecting Drive. |
| S02 · Malicious text or embeds (XSS) High · needs testing | Text, links, HTML, imports and embeds appear in the interface. There is escaping and a custom filter based on text patterns. This does not prove that every rendering path is safe. | Test all paths with unsafe input. Check URL protocols and HTML in each context. Use a maintained sanitizer where HTML is needed. Add a suitable Content Security Policy as an extra layer. |
| S03 · Unintended actions from another site (CSRF) High · missing central layer | No general CSRF token, Origin or Fetch Metadata check was found in the central request handling. SameSite=Lax helps, but does not cover every situation. OAuth state protects the callback, not every other action. | Protect changes, deletion, administration and disconnection with an appropriate CSRF check. Test foreign origins and another subdomain on the same site. Ordinary read requests must not perform destructive actions. |
| S04 · Access to another account High · needs testing | The code separates users and checks sign-in. A bug in one route or an incorrectly used file ID could still cross that boundary. | Use two test accounts of your own. Check reads, writes, media, files, tickets, folders and deletion actions with IDs from the other account. The server must deny access without exposing content. |
| S05 · Shared or compromised device High · design limitation | IndexedDB contains readable working copies. The service worker caches the notes page, among other things, for offline use. Logging out revokes the server session; it does not prove that all local data has been erased. The browser-only store currently uses the non-account-suffixed name paper_documents. Account-scoped namespaces are a mandatory rebuild correction; see the documented limitation. | Test logout, account switching and offline reload in a shared browser profile. Explain what remains locally. Do not silently erase unsent work. Protect the device and define a session duration policy. |
| S06 · Broad Drive permissions and token loss High · observed in code | The app requests drive.readonly and drive.file. Read permissions are not limited to the FileLoveNote folder. An encrypted token remains sensitive if the server key also leaks. | Explain the permissions clearly. Choose the smallest set of permissions that allows the intended use of existing files. Test disconnection and access revocation; keep tokens out of errors, browser storage and logs. |
| S07 · Large uploads and many requests High · observed in code | The app limit is 10 GiB per file; the general request limit is 40 GiB. No general per-user or per-IP request rate limit was found in the central server code. Limits in the active proxy were not checked. | Set per-route limits for bytes, time, concurrency and requests. Stream large files. Limit memory, disk space and expensive AI or media operations. Test limit violations without using real large private files. |
| S08 · Unsafe files and dependencies High · not scanned | An allowed extension or declared MIME type does not prove that the bytes are safe. PDF, EPUB, media and HTML processing use custom or external parsers. No scan for known dependency vulnerabilities was performed for this chapter. | Check type and content as appropriate for the file. Keep Bun, packages, ffmpeg and Whisper up to date. Isolate risky processing and give processes minimal permissions. Do not send private files to an external scanner without consent. |
| S09 · Headers, caching and infrastructure High · not checked live | The central responder does not set a general CSP, anti-framing policy or X-Content-Type-Options. The active proxy may add headers; that was not established here. | Check HTTPS, security headers, app port access and cache policies for private responses. Also test shared proxy caches and offline caches. Keep the data folder and secrets out of public reach. |
| S10 · Data sent to AI, external content and old copies Medium to high · needs testing | Requested AI features may send content to Gemini. External links and embeds may contact third parties. Old logs, backups and browser copies do not automatically comply with the new storage contract. | Make external content sharing visible. Treat document text and AI output as untrusted input. Recheck permissions and safe rendering. Define retention periods and inspect logs and backups for content and secrets. |
OWASP recommends safe output handling and HTML filtering against XSS. SameSite is an extra layer alongside appropriate CSRF protection. Local browser storage is not a secret vault; see OWASP on IndexedDB.
File extension and MIME type are not enough for safe uploads; see the OWASP file upload guidance. Google describes the scope of the requested Drive permissions. These sources provide guidance; they do not prove that the app complies.
How to check security
Apply the required startup gates, request limits and recovery procedures in 14 Setup & Operations, 11 API Contracts and 12 Failure & Recovery. Their implementation must be tested; this chapter does not turn missing protections into existing ones.
- Configuration: in a test environment, check behavior without a secret, without a Secure cookie and with an expired or modified session. Record missing protections as open items.
- Access: test with two accounts and a non-admin. Use only your own synthetic IDs to try crossing the account boundary; also check background requests and sync tickets.
- Input: test text, imports, embeds, filenames and URLs. Check that input remains text or is rejected and does not execute scripts.
- Requests: test unwanted origins, invalid OAuth state, expired sessions, oversized requests and limits. An error must not reveal a token or note content.
- Storage: check IndexedDB, service worker cache, logs and temporary packages after sync, logout and account switching. Use test text that is easy to find.
- Deployment: check real headers, TLS and proxy limits separately. Run an up-to-date dependency scan. A passing local functional test does not replace this check.
Use your own test data and BrowserOS Neo for browser checks. Do not perform attack tests on a production environment or account without authorization. Link the results to V01: sign-in and isolation, V11: files and the sync scenarios in Verification.
Prompt for the builder
Read prompt/security.html together with Solution Intent and Solution Design.
For each risk S01–S10, distinguish existing protection, missing protection and investigation not yet performed.
Keep Google Drive as the only external file storage and preserve the browser/Drive note contract.
For the chosen scope, record: attack scenario, intended protection and a check using your own test data.
Describe an improvement as implemented only after the code has changed and the relevant check passes.
Report functional tests, security tests, dependency scanning and live infrastructure checks separately.
Do not publish or change an external account without an instruction.Where does this explanation come from?
The builder does not need the original code to read this chapter. For verification in this repository, these are the main source files:
src/routes/auth-routes.js,src/services/google-auth-service.jsandsrc/session/session-store.js: sign-in, state, session rotation and cookies.src/http/auth-middleware.js,src/app/request-scope.js,src/app/service-graph.jsandsrc/routes/admin-routes.js: account boundaries and administration.src/services/google-drive-auth-service.js,src/services/encrypted-sync-station.jsandsrc/services/drive-notes-service.js: Drive permissions, tokens and temporary sync.src/config/env.js,src/http/responder.js,src/http/runtime-server.jsandsrc/app/constants.js: configuration, headers and limits.src/services/shared-utils-service.js,src/routes/google-drive-routes.js,src/routes/offline-routes.jsandclient/paper/drive-document-store.ts: input, uploads and browser storage.
Reviewed on 23 October 2026. Update this explanation when permissions, sessions, storage or network protection changes.