Commit Graph

2 Commits

Author SHA1 Message Date
hell 95bfd42ff8 fix: four defects in the code that came with #150-#152
- app.py handed every /static response `max-age=15552000, immutable`, but
  only the assets built through static_version() carry ?v=<mtime>. The
  favicon, the icons, qrcode.min.js, searchable-select.js, the vendored
  CodeMirror bundles and ReDoc keep one URL forever, so a redeploy could not
  reach a browser that had already cached them. Immutable is now limited to
  fingerprinted URLs; the rest get an hour plus a revalidation.
- WireGuardManager._apply_bw_limits ran `docker cp ... && docker exec ...`
  through run_sudo_command, which only privileges the head of the chain, so
  the exec ran as the unprivileged SSH user. Wrapped in one `sh -c`, the
  pattern tests/test_sudo_command_chains.py enforces.
- toggleConnection() restored the row only in the error path. When
  loadConnections() fails after a successful toggle, the row it was going to
  rebuild stays disabled with a spinning bulb. Restore now runs in `finally`.
- The add-user modal starts on role "none", where the password is optional,
  but the label said "required" until the select fired its change event.

Tests: the bandwidth chain joins the sudo-chain suite, and a new
tests/test_static_cache.py pins both cache lifetimes.
2026-09-10 00:17:21 +07:00
hell 04c623cc87 fix: run privileged command chains inside one shell
`run_sudo_command("a && b")` builds `echo <pw> | sudo -S -p '' a && b`, so
sudo elevates only the first command of the chain. Everything after `&&` runs
as the unprivileged SSH user and fails on anything root-owned. Four call sites
were affected:

- backup_manager: restoring a protocol backup copies the archive out of
  /opt/amnezia/backups, which is root-owned, so the restore died with
  `cp: cannot stat '/opt/amnezia/backups/awg2/...': Permission denied` right
  after reporting that it had found the archive.
- app.py: the inline backup-download chain has the same shape.
- dns_manager: `docker network create` and the per-container attach loop -
  the attach silently never happened, so containers started before AmneziaDNS
  were left off amnezia-dns-net.
- adguard_manager `_ensure_network`: same.

Each chain now goes to one shell via `sh -c '<chain>'` with the arguments
quoted, so the whole chain runs under sudo. tests/test_sudo_command_chains.py
records the commands a fake SSH layer receives and asserts every privileged
chain is wrapped, plus a unit check on the helper itself, so a new bare `&&`
in these paths fails the suite.

The one error in the suite on this branch is the pre-existing
`import pytest` in tests/test_playwright_e2e.py, unrelated to this change
and fixed separately.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 22:07:55 +07:00