Skip to content

Harden patch_chatgpt_providers.py - #1

Closed
ydeng11 wants to merge 3 commits into
Keksuccino:mainfrom
ydeng11:harden-provider-patch-installer
Closed

Harden patch_chatgpt_providers.py#1
ydeng11 wants to merge 3 commits into
Keksuccino:mainfrom
ydeng11:harden-provider-patch-installer

Conversation

@ydeng11

@ydeng11 ydeng11 commented Jul 31, 2026

Copy link
Copy Markdown

Summary

Hardens patch_chatgpt_providers.py (the macOS installer that injects the custom model-provider picker + per-model provider routing into the desktop Codex client) based on an advisor review. All six findings are fixed; the version-sensitive exact-hunk design and every embedded CENTRAL_DIFF* / PICKER_DIFF* hunk are untouched (byte-identical).

Changes

  1. Sudo config ownership (blocker) — When run via sudo, the default ~/.codex/desktop-model-providers.json is now owned by the invoking user (file + freshly created parent dirs). Custom --config paths are never reassigned to SUDO_USER: existing files preserve their owner, missing files stay root. Required chown failures raise PatchError instead of being swallowed.

  2. Robust restorerestore_backup() restores into a sibling staging directory, verifies it, then atomically swaps. Any failure raises a combined diagnostic naming backup, failed_copy, and staging (including swap, move-back, and cleanup error details) and never silently leaves the app path absent.

  3. Honest READY detection — "Already installed" is only reported after verify_installed_bundle() passes: ASAR header hash vs Info.plist, V3 marker present, V2 marker absent (mixed markers rejected), picker symbol present, and codesign --verify --deep --strict succeeds (stderr preserved in the error). The fresh-install path uses the same unified check.

  4. Strict, JS-aligned config validationversion must be the integer 1 (bools/1.0/"1"/None rejected), string checks precede set membership (no unhashable TypeError), and mapping values are matched untrimmed to mirror codexNormalizeProviderRoutingConfig. Config state is modeled as keep/create/replace: only keep validates the existing file, so --overwrite-config repairs malformed JSON instead of rejecting it.

  5. Deferred mutations — Target-app processes are stopped only after the patched asar is packed and verified (just before backup); the provider-routing config is committed only after codesign + final ASAR checks, so an unsupported or failed install never disturbs a running app or clobbers user configuration.

  6. Executable from CFBundleExecutablestop_target_app_processes() no longer hardcodes Contents/MacOS/ChatGPT; it derives the name from Info.plist.

  7. --self-test mode — 28 unit tests (no app/network access): config validation/lifecycle, diff mechanics (render_unified_diff, parse_hunks, derive_versioned_diff, unique_candidate, contains_marker chunk boundaries), synthetic per-variant fixtures proving unique layout matching + repeat-application rejection, fake-ASAR READY-state rejection (hash mismatch / mixed markers / missing picker), codesign-failure conversion, restore diagnostics, and ownership-policy tests with mocked root/SUDO_USER.

Verification

  • python3 -m py_compile patch_chatgpt_providers.py — clean
  • python3 patch_chatgpt_providers.py --self-test — all 28 tests pass, exit 0
  • python3 patch_chatgpt_providers.py --help — renders, lists --self-test

PLAN.md is included as the plan/design record for the change.

- Fix sudo config ownership (SUDO_USER policy, chown failures raise PatchError)
- Robust staging-sibling restore with combined diagnostics
- README verification: hash vs plist, mixed markers, picker symbol, signature
- Strict JS-aligned config validation (keep/create/replace states)
- Defer process shutdown and config commit until app verified
- Derive target executable from CFBundleExecutable
- Add --self-test mode (24 tests) + PLAN.md
@ydeng11 ydeng11 changed the title Harden patch_chatgpt_providers.py per advisor review Harden patch_chatgpt_providers.py Jul 31, 2026
ydeng11 added 2 commits July 31, 2026 15:44
The 26.727 rename tables mapped the injected loaders' host-file requests
from tp() to rp(), but rp() only dispatches the app-server request map
(model/list, thread/skill requests). The real bundle calls codex-home and
read-file via the local-host bridge bp() (vscode://codex/<method>), so the
config always failed to load and the picker fell back to the embedded
OpenRouter config. Revert those two renames to bp(); verified in the
installed bundle (bp(codex-home) x5, bp(read-file) x7, rp(...) 0) and all
28 self-tests pass.
The upgraded 26.727.51351 / build 6119 bundle renamed the app-server
params getter (rSe -> nSe) and rewrote the model-picker menu (the item
component now renders the config's extras slot natively, so the separate
item-component patch is unnecessary). Adds:

- CENTRAL_DIFF_6119: 26.727 central diff with the nSe rename.
- PICKER_DIFF_6119: injection + model-list prepend + React binding
  adapted to the rewritten picker, symbol-renamed (CZ/SZ, _z/mz).
- PATCH_VARIANTS entry so the installer matches build 6119 bundles.

Verified: 28 self-tests pass, variant applies uniquely to the real 6119
bundle (prettier + node clean), install verified via the READY path.
@ydeng11 ydeng11 closed this by deleting the head repository Aug 2, 2026
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