Skip to content

fix(cli): redact secrets from audit logs and let update --check run without root #88

Closed

Description

@entelecheia

Context

Audit of the CLI on 2026-11-09 (tracking issue #98). Both findings verified on main at 949d449.

  • verified: requireRootAndLock writes the full command line to the audit log (internal/cli/runtime.go:88), and newRunner stores os.Args as the backup-session note (runtime.go:134), which rootfiles rollback prints (rollback.go:34). --tunnel-token, tunnel setup TOKEN and user passwd --password therefore land in plain text in root-owned files.
  • verified: update is in mutatingCommands and preflight only exempts --dry-run (runtime.go:64-70). rootfiles update --check, documented without sudo (README "Self-update"), fails with "must run as root". tunnel update --check has the same problem.

Implementation starts only on the owner's go-ahead.

Decisions

  • Redact by flag name and positional slot in one helper; never try to detect secrets by value.
  • Read-only flags (--check) are exempt from root/lock the same way --dry-run is.
  • update --check and tunnel update --check exit 2 when an update is available (same meaning as check drift), 0 when current. This is a behaviour change for scripts and goes in the release notes.
Decision Owner Limits Revisit when
Exit 2 on "update available" AI proposes, user picks Only the --check form A fleet or schedule consumer relies on exit 0
  • 2026-11-09: update --check and tunnel update --check exit 2 when an update is available, 0 when current. Basis: owner decision in the planning session.

Design

  • redactArgs(args []string) []string in internal/cli/runtime.go: replaces the value of --tunnel-token, --password (both --flag value and --flag=value) and the positional TOKEN of tunnel setup with ***. Used by the audit args attribute and the backup note.
  • preflight skips root/lock when the command has a read-only flag set. Mark it with a cobra annotation on the flag (rootfiles/readonly) rather than a hardcoded list.

Changes

  • internal/cli/runtime.go: redactArgs, read-only-flag exemption.
  • internal/cli/upgrade.go, internal/cli/tunnel.go: annotate --check; exit 2 when newer.
  • internal/cli/runtime_test.go: table tests for redaction and the exemption.
  • README.md: self-update section states the exit codes.

Acceptance criteria

  • AC1: audit log record and backup note for apply --tunnel-token X, tunnel setup X and user passwd --password X contain no X.
  • AC2: rootfiles update --check and rootfiles tunnel update --check run as non-root and take no lock.
  • AC3: --check exits 2 when a newer version exists, 0 otherwise.

Tasks

  • T1: redactArgs + tests (AC1)
    • Done when: go test ./internal/cli -run Redact passes.
  • T2: read-only flag exemption + exit codes (AC2, AC3)
    • Done when: go test ./internal/cli -run 'Preflight|UpdateCheck' passes with a non-root geteuid stub.
  • T3: release note bullet under Improved/Fixed.

Out of scope

  • Rotating secrets already written to existing audit logs (operators can truncate /var/log/rootfiles*.jsonl; mention in release notes).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions