Skip to content

fix(update): admit umask-restricted release payloads and re-sync orca-plugin.json - #2821

Merged
namastex888 merged 1 commit into
devfrom
fix/update-admission-umask
Aug 30, 2026
Merged

namastex888 merged 1 commit into
devfrom
fix/update-admission-umask

Conversation

@namastex888

Copy link
Copy Markdown
Contributor

Problem

genie update --dev (5.260816.2 → 5.260829.4) fails on any host whose umask strips group/other bits (e.g. 0077):

▸ Promoting verified release generation...
✖ Update failed: admitted install payload content does not match the authenticated source

Root cause (src/lib/install-promotion.ts → admitExternalInstallStaging): tar -xzf extracts genie as 0700 under that umask. Admission digests the external payload (the digest binds mode & 0o777, so it records 0700), cpSyncs it into the held stage, then fchmods the copy to 0755 — and only then compares digests. They can never agree. The tarballs themselves are fine (./genie is 0755 in every published release); the bug has shipped since v5.260720.1.

Fix

Normalize the external genie to 0755 through an owned O_NOFOLLOW descriptor before the authenticated digest is taken (normalizeExternalBinaryMode). The external root is our own private extraction sandbox and the copy was always going to be 0755, so this changes no security posture — it moves an existing normalization ahead of the hash.

Verified against the real genie-5.260829.4-linux-x64-glibc.tar.gz extracted under umask 077: unpatched module → exact error above; patched → admitted + verifyAdmittedInstallStagingPayload passes.

Tests

  • New regression test: external genie at 0700 → admission succeeds, both sides end at 0755, digest guard holds. Fails on dev with the exact user-facing error.
  • Two existing tests assumed a permissive umask (mkdirSync({mode:0o755}), writeFileSync({mode:0o644}) are umask-filtered); they now chmod the bits they assert so the suite is umask-independent.

Also: plugins/genie/orca-plugin.json → 5.260829.8

This PR's CI (and the dev tarball build for v5.260829.8) fails with committed version mismatch in plugins/genie/orca-plugin.json: expected 5.260829.8, got 5.260829.7. version.yml is a workflow_run workflow, so GitHub executes main's copy — which still syncs "the exact seven version fields" and doesn't know orca-plugin.json (dev's copy from #2812/#2816 lists nine). Every dev bump will keep leaving it behind until main's workflow is updated; #2816 hand-patched .7 for the same reason. This PR hand-patches .8 so CI is green now; the durable fix is promoting the workflow change to main (see companion PR against main).

Note for operators on affected hosts

The installed 5.260816.2 binary runs the buggy admission itself, so genie update from it will still fail even after this ships. Use install.sh (it promotes via the new binary's __install-promote) to get onto the first release containing this fix; subsequent genie update runs are then fine.

🤖 Generated with Claude Code

https://claude.ai/code/session_013gGxGgKskzyzr1HRUB6cV3

…-plugin.json

`genie update` failed on every host whose umask strips group/other bits
(e.g. 077) with "admitted install payload content does not match the
authenticated source". `tar -xzf` extracts the Genie binary as 0700 there;
admission digested the external payload (mode-bearing digest, 0700), copied
it, then fchmod'ed the held copy to 0755 — so the two digests could never
agree. Normalize the external binary to 0755 through an owned O_NOFOLLOW
descriptor *before* the authenticated digest is taken; the external root is
our own private extraction sandbox and the copy was going to be 0755 anyway.

Regression test reproduces the exact failure; two existing tests that
assumed a permissive umask now chmod the bits they assert.

Also bump plugins/genie/orca-plugin.json to 5.260829.8: main's version.yml
(the copy workflow_run actually executes) still syncs seven JSON files and
leaves orca-plugin.json behind, which is what broke the dev tarball build
and this PR's CI. The workflow fix on dev only takes effect once promoted
to main.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013gGxGgKskzyzr1HRUB6cV3
@coderabbitai

coderabbitai Bot commented Aug 30, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 68114910-137b-45ff-b31d-1b089e2deade

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 30, 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-08-30T00:47:02.662747Z 8f1c2f7 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: 8f1c2f782b

ℹ️ 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".

if (expectedVersion === null) {
throw new InstallPromotionError('external install staging requires an expectedVersion to authenticate VERSION');
}
normalizeExternalBinaryMode(externalStagingRoot);

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 Preserve install.sh's staged binary identity

When install.sh runs as a non-root user with umask 077, the extracted binary is 0700; installPromoteCommand records that exact identity at src/genie-commands/install-promote.ts:268, but this call changes its mode to 0755. The verifier at lines 318–324 then compares the mutated file with the pre-normalization identity (which includes mode and digest) and rejects it, so the documented install.sh bootstrap path for users affected by the old updater still fails. Normalize before capturing mutation authority, or refresh the bound identity without weakening the inode/content checks.

Useful? React with 👍 / 👎.

@namastex888
namastex888 merged commit a4fddce into dev Aug 30, 2026
18 checks passed
@namastex888
namastex888 deleted the fix/update-admission-umask branch August 30, 2026 00:47
namastex888 added a commit that referenced this pull request Aug 30, 2026
…ves umask 077

genie update / install.sh extracted the signed tarball with a bare
'tar -xzf'. As non-root, tar applies the caller umask to every member,
so under umask 077 the archived 0755 'genie' landed as 0700 and every
skill, plugin file and script lost its group/other bits too.

#2821 made admission tolerate the binary case by normalizing the external
binary to 0755 before digesting. This closes the root cause one layer up:
extract with 'tar -xzpf' in both extractors so the signed archive's
recorded modes are reproduced for the whole payload regardless of the
caller's umask.

- regression test: extraction keeps 0755/0644 under umask 077
- install.sh extract_and_link body must carry -p (static assertion)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018QrkgYMEEhrWTUo5E7Nkjg
namastex888 added a commit that referenced this pull request Aug 30, 2026
…ves umask 077 (#2824)

genie update / install.sh extracted the signed tarball with a bare
'tar -xzf'. As non-root, tar applies the caller umask to every member,
so under umask 077 the archived 0755 'genie' landed as 0700 and every
skill, plugin file and script lost its group/other bits too.

#2821 made admission tolerate the binary case by normalizing the external
binary to 0755 before digesting. This closes the root cause one layer up:
extract with 'tar -xzpf' in both extractors so the signed archive's
recorded modes are reproduced for the whole payload regardless of the
caller's umask.

- regression test: extraction keeps 0755/0644 under umask 077
- install.sh extract_and_link body must carry -p (static assertion)


Claude-Session: https://claude.ai/code/session_018QrkgYMEEhrWTUo5E7Nkjg

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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