Skip to content

fix: address config, SIXEL, session, and IPC audit findings - #82

Merged
simota merged 9 commits into
mainfrom
fix/audit-2026-09-10
Sep 10, 2026
Merged

simota merged 9 commits into
mainfrom
fix/audit-2026-09-10

Conversation

@simota

@simota simota commented Sep 10, 2026

Copy link
Copy Markdown
Owner

Prevent automatic attachment to reused pane IDs after a server restart, bound SIXEL rendering work, and make configuration and persistence updates reliable.

  • Compare the server process identity before reconnecting; a changed identity requires a manual retry. Keep reconnect tests independent of scrollback worker scheduling.
  • Limit SIXEL pixel writes independently of canvas size, counting only set bits so sparse multicolor bands remain valid while excessive overdraw is rejected.
  • Use unique staging files for session saves and serialize empty token-file recovery with an advisory lock.
  • Make single-theme and light/dark-pair overrides mutually exclusive. Preserve font fallbacks when changing the primary family, and persist font resets after trailing includes.
  • Write through dangling configuration symlinks while preserving the links and rejecting cycles.

Compatibility

The serverInstanceId hello field is additive. Older servers that omit it retain the previous reconnect behavior. Token provisioning leaves a server-token.lock file beside the token; filesystems that cannot acquire the advisory lock retain the existing unlocked behavior with a warning.

Validation

  • cargo test --workspace — 2,625 passed, 15 ignored, on macOS outside the sandbox (shared-memory tests require this).
  • cargo test -p noa-config --lib writer::tests — 27 passed.
  • cargo test -p noa-grid --lib sixel::tests — 15 passed.
  • cargo test -p noa-app --lib remote_attach::tests — 50 passed.
  • Regression cases for trailing-include resets, sparse SIXEL bands, and scrollback connection accounting failed before the fixes and passed afterward.
  • cargo build --workspace --locked
  • cargo fmt --all -- --check
  • git diff --check

Add a per-process `serverInstanceId` to the `noa.hello` response (serde
default, protocol version unchanged). The remote-attach connection manager
remembers the instance it first attached to; when an automatic reconnect
reaches a different instance it drops the control socket before
`reserve_attach`/`resize_pane` and publishes `Detached`, so a pane id
reused after a restart never receives input meant for the old pane. A
manual retry adopts the new instance.

Claude-Session: https://claude.ai/code/session_01YTGJ217L3Zc1eZAGeyftpm
Repeated `!N` overdraw followed by `$` could run hundreds of millions of
pixel writes under the terminal lock without growing the image. Charge
each non-blank sixel column against a work budget of twice the largest
admissible image and reject with `TooBig` once exceeded.

Claude-Session: https://claude.ai/code/session_01YTGJ217L3Zc1eZAGeyftpm
Two processes sharing `session.json.tmp` could interleave a truncating
write with the other's rename and publish a corrupt file. Stage in
`.session.json.<pid>.<seq>.tmp` opened with `create_new`, sync, then
rename; remove the staging file on failure.

Claude-Session: https://claude.ai/code/session_01YTGJ217L3Zc1eZAGeyftpm
Concurrent initializers that both saw an empty token file raced their
`remove_file` calls and could delete the token the other had just
published. Hold an exclusive `File::lock` on `<token>.lock` across
read → empty-file recovery → publish; fall back to unlocked with a
warning if the lock cannot be taken.

Claude-Session: https://claude.ai/code/session_01YTGJ217L3Zc1eZAGeyftpm
…, dangling symlink writes

- `ConfigOverrides.theme` is a single tri-state `Option<ThemeSetting>`;
  a later single name clears an earlier light/dark pair and vice versa,
  across parse, merge and apply.
- An empty `font-family*` value resets the list (Ghostty parity) instead
  of emitting a diagnostic; the settings panel writes that reset line
  when a family had been configured.
- A non-empty `font-family*` update replaces the primary slot (first
  entry after the last reset) so fallbacks are kept.
- `write_config_updates` resolves symlinks via `symlink_metadata`/
  `read_link` (relative targets, 40-hop cap), so a link whose target
  does not exist yet writes the target and keeps the link.

Claude-Session: https://claude.ai/code/session_01YTGJ217L3Zc1eZAGeyftpm
Append font-family resets after later includes so child font entries cannot survive a reset. Cover all family variants and repeated saves.
Sparse color passes paint disjoint rows of a sixel band. Charge set bits so valid multicolor images fit their pixel budget while repeated overdraw remains bounded.
Advance scripted server instances only for Attach connections so backfill worker timing cannot change reconnect counts or the simulated restart.
Apply rustfmt to the existing audit changes so the workspace passes the CI formatting check.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 10, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-10T02:13:58.155701Z 39277b4 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 39277b478b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

scheduled_retry = true;
continue;
};
attached_instance = Some(control.server_instance_id());

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Validate the server instance for scrollback fetches

When the server restarts after an attachment but before its scheduled scrollback fetch, attached_instance remains local to the connection manager while run_scrollback_worker opens an independent control connection without comparing its server_instance_id. If the old attach has not detected its disconnect yet, the generation check still succeeds and scrollback from the new server's reused pane ID is merged into the terminal; the later detach does not remove that unrelated history. Share the expected instance with the worker and reject a mismatch before get_scrollback or merging.

Useful? React with 👍 / 👎.

@simota
simota merged commit 7565e38 into main Sep 10, 2026
1 check passed
@simota simota mentioned this pull request Sep 10, 2026
@simota
simota deleted the fix/audit-2026-09-10 branch September 10, 2026 23:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant