Skip to content

Security hardening (OWASP Top 10 review) - #25

Merged
benjamindehli merged 4 commits into
mainfrom
fix/security-hardening
Oct 31, 2026
Merged

benjamindehli merged 4 commits into
mainfrom
fix/security-hardening

Conversation

@benjamindehli

Copy link
Copy Markdown
Owner

A review of the local bridge and web editor against the OWASP Top 10 (2025) found one high-severity issue and three hardening gaps. One commit per finding.

1. Refuse cross-site requests (A01 Broken Access Control, high)

The Host check blocked DNS rebinding, but the bridge accepted POSTs from any website open in the same browser (cross-site request forgery). Responses were unreadable, but every action still ran, including ones that change banks stored on the device. Recent Chrome versions ask before a site can reach localhost; Safari and Firefox don't.

  • New Handler._cross_site() on every POST: a browser Origin must be exactly the bridge's own origin. Without one, Sec-Fetch-Site: cross-site is refused.
  • The menu-bar app, CLI tools and tests send neither header, so they keep working.

2. Forbid framing and MIME sniffing (A02 Security Misconfiguration)

Every response now sends X-Frame-Options: DENY, Content-Security-Policy: frame-ancestors 'none' and X-Content-Type-Options: nosniff, so another site can't load the editor in a hidden frame and trick clicks onto RESTORE or a confirm dialog.

3. Size limits (A10 Mishandling of Exceptional Conditions)

  • Request bodies over 256 MiB get a 413 before being read.
  • Backup ZIP imports are capped at 64 MiB unpacked and 1,000 files, checked up front and again while streaming (zip bombs).
  • Failed imports no longer leave a half-written backup behind, and a corrupt ZIP is a 400 instead of a 500.

4. Verify the daemon's backup folder (A01, low)

The root daemon stores backups under /Users/Shared, which every account can write to. It now creates the path with 0755 and checks every part of it: no symlinks, owned by root, not writable by others, sticky shared base. If anything's off, it logs a warning and uses a root-only folder under /Library/Application Support instead. Existing installs pass the check unchanged.

Checked and fine

Path traversal (backup names, static files, zip slip), XSS (every device/file-derived name is escaped), DNS rebinding, loopback-only binding.

Testing

  • New sections in test_bridge.py (origin guards, security headers, body cap, zip import caps) and a new test_daemon_dirs.py
  • Offline suite, ruff, e2e smoke; Python 3.8 syntax check
  • Real browser: a page on another origin got 403 on a cross-site POST; framing app.html was blocked by the browser (frame-ancestors 'none')
  • macOS: secure_dir returns True for the existing /Users/Shared/DehliMusikk install (run as root)

@benjamindehli benjamindehli added bug Something isn't working enhancement New feature or request dependencies Pull requests that update a dependency file labels Oct 31, 2026
@benjamindehli benjamindehli self-assigned this Oct 31, 2026
@benjamindehli benjamindehli added this to the v1.17.4 milestone Oct 31, 2026
@benjamindehli
benjamindehli merged commit a3d7fc9 into main Oct 31, 2026
9 checks passed
@benjamindehli
benjamindehli deleted the fix/security-hardening branch October 31, 2026 20:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working dependencies Pull requests that update a dependency file enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant