Skip to content

fix(security): restrict tunnels.json file permissions (followup to #814) #1165

@arnoldwender

Description

@arnoldwender

Finding from my 2026-04-15 comment on #809, greenlit by @Kesshite on 2026-04-16 but never filed as a standalone issue. Splitting off per @bensig's request in #809.

Summary

~/.mempalace/tunnels.json (introduced in #790) is created via plain open(..., "w") without a follow-up chmod. The parent directory ~/.mempalace/ is created via os.makedirs() without mode=0o700. On Linux with the default umask 022, both the file and its parent end up world-readable (0o644 / 0o755).

This is the same class of issue as the 6 locations already hardened in #814tunnels.json was missed because it landed after the file-permissions audit.

Affected version

v3.3.3 — verified against develop @ 8ac98f0, file mempalace/palace_graph.py:315-328.

Impact

Medium — post-auth local. The contents of tunnels.json are sensitive metadata: cross-wing relationships reveal which projects, people, and rooms the user has explicitly connected. On multi-user systems (shared dev servers, classroom/lab Linux workstations, shared hosting), any local user can read this file with the current permissions.

Reproduction

python -c "from mempalace.palace_graph import create_tunnel; create_tunnel('a','r1','b','r2')"
ls -la ~/.mempalace/tunnels.json   # Linux: -rw-r--r-- (world-readable)
ls -ld ~/.mempalace                # Linux: drwxr-xr-x (world-traversable)

Suggested fix

Match the pattern established in #814. In mempalace/palace_graph.py:

def _save_tunnels(tunnels):
    parent = os.path.dirname(_TUNNEL_FILE)
    os.makedirs(parent, exist_ok=True)
    try:
        os.chmod(parent, 0o700)
    except (OSError, NotImplementedError):
        pass  # Windows / unsupported FS
    tmp_path = _TUNNEL_FILE + ".tmp"
    with open(tmp_path, "w", encoding="utf-8") as f:
        json.dump(tunnels, f, indent=2)
        f.flush()
        try:
            os.fsync(f.fileno())
        except OSError:
            pass
    os.replace(tmp_path, _TUNNEL_FILE)
    try:
        os.chmod(_TUNNEL_FILE, 0o600)
    except (OSError, NotImplementedError):
        pass

Happy to submit a PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingsecuritySecurity related

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions