Skip to content

fix(windows): classify localized scheduler denial (#591) - #698

Merged
lidge-jun merged 4 commits into
devfrom
fix/591-windows-schtasks-locale
Jul 29, 2026
Merged

fix(windows): classify localized scheduler denial (#591)#698
lidge-jun merged 4 commits into
devfrom
fix/591-windows-schtasks-locale

Conversation

@Ingwannu

@Ingwannu Ingwannu commented Jul 29, 2026

Copy link
Copy Markdown
Owner

Summary

  • classify the owned schtasks /create /tn opencodex-proxy /xml ... /f status-1 failure as access denied when the effective Windows token is known to be non-elevated
  • preserve the existing localized-text detector, but fail closed when the native token probe is unavailable
  • restore the existing machine marker used by Startup Safety's fixed UAC retry, while excluding query/run/delete, WinSW, file-write, and foreign-task failures
  • document the Windows service/UAC workflow in every shipped CLI locale and record the security decision in the structure SOT

Addresses #591.

Root cause

On a Chinese Windows installation, schtasks.exe returned localized access-denied output in the system OEM code page. Bun decoded the captured bytes as UTF-8 mojibake, so the English/German text matcher classified the structured scheduler error as reason: "other". The CLI lost actionable guidance and the dashboard did not receive OCX_ERROR_CODE=WINDOWS_SCHTASKS_CREATE_ACCESS_DENIED, so its existing trusted UAC transaction was never selected.

The reporter's later diagnostics also show that the elevated installation itself succeeded. Their remaining Startup Safety warning followed ocx restore, which intentionally returns Codex to native/external routing; ocx restore back is the correct way to re-enable OpenCodex routing.

Security boundary

This does not treat arbitrary exit code 1 as permission denial. The fallback requires all of:

  1. the fixed /create operation,
  2. the owned task name opencodex-proxy,
  3. an XML task path and /f,
  4. process exit status 1, and
  5. a native effective-token probe that explicitly reports non-elevated.

Unknown token state remains reason: "other". The elevated transaction continues to resolve trusted System32 executables, accept only the fixed scheduler protocol, and fail closed on unexpected outcomes.

Microsoft documents IsUserAnAdmin as a wrapper over CheckTokenMembership; the latter only reports an enabled Administrator SID, so a UAC-filtered deny-only SID does not grant this signal:

Validation

  • bun x tsc --noEmit
  • bun test --isolate tests — 5901 passed, 1 Windows-only runtime test skipped, 0 failed
  • bun run privacy:scan
  • cd docs-site && bun install --frozen-lockfile && bun run build — 146 pages built
  • targeted Windows elevation/startup-action tests — 34 passed, 0 failed

dev2-go transition

dev has no go/ tree, so this PR cannot carry the native Go scheduler counterpart. dev2-go does have go/internal/service/install.go, whose scheduler path currently returns raw localized schtasks.exe failures and has no equivalent UAC marker/transaction. Per MAINTAINERS.md, merging this PR into dev is not the end of the integration task: the merger must assess and port the behavior to dev2-go in the same session, or open a needs-go-port tracking issue naming this source commit (cdc16e5a) before closing the merge work.

Summary by CodeRabbit

  • Bug Fixes

    • Improved Windows ocx service install error handling for localized environments by recognizing “access denied” only for the app’s own fixed Task Scheduler create attempt and confirming non-elevated token state.
    • Startup Safety now triggers UAC recovery only when elevation probe results are known and appropriate; otherwise it preserves the original scheduler error and avoids automatic-elevation markers.
  • Documentation

    • Updated CLI docs (JA/KO/EN/RU/ZH-CN) and Startup Safety notes with Windows Task Scheduler/UAC fallback rules for non-English systems.
  • Tests

    • Expanded Windows elevation probing and error-classification test coverage, including locale-independent behavior and marker scoping.

@github-actions github-actions Bot added the bug Something isn't working label Jul 29, 2026
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 60737426-e17d-40bd-bddf-bcbbb6cb54ed

📥 Commits

Reviewing files that changed from the base of the PR and between 9338550 and 34688f6.

📒 Files selected for processing (5)
  • docs-site/src/content/docs/ja/reference/cli.md
  • docs-site/src/content/docs/ko/reference/cli.md
  • docs-site/src/content/docs/reference/cli.md
  • docs-site/src/content/docs/ru/reference/cli.md
  • docs-site/src/content/docs/zh-cn/reference/cli.md

📝 Walkthrough

Walkthrough

Windows Task Scheduler elevation detection now uses a locale-independent token probe. Access-denied recovery is restricted to the owned opencodex-proxy creation command, with fail-closed tests and multilingual Windows guidance added.

Changes

Windows elevation and Task Scheduler recovery

Layer / File(s) Summary
Effective-token elevation probe
src/lib/windows-elevation.ts
Adds an injectable probe, native IsUserAnAdmin detection, and null for unknown elevation state.
Owned scheduler access-denied classification
src/lib/windows-elevation.ts
Requires the owned /create opencodex-proxy shape, exit status 1, and a confirmed non-elevated token for the fallback classification.
Behavior validation and platform guidance
tests/windows-elevation.test.ts, structure/05_gui-and-management-api.md, docs-site/src/content/docs/.../reference/cli.md
Tests command scoping and fail-closed states; documentation describes UAC recovery and manual installation guidance.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant schtasks.exe
  participant isWindowsSchtasksAccessDeniedError
  participant isCurrentWindowsProcessElevated
  participant toWindowsSchtasksError
  schtasks.exe->>isWindowsSchtasksAccessDeniedError: Return scheduler status and stderr
  isWindowsSchtasksAccessDeniedError->>isCurrentWindowsProcessElevated: Check current process elevation
  isCurrentWindowsProcessElevated-->>isWindowsSchtasksAccessDeniedError: Return elevated, non-elevated, or unknown
  isWindowsSchtasksAccessDeniedError->>toWindowsSchtasksError: Classify as access-denied or other
Loading

Possibly related issues

Possibly related PRs

  • lidge-jun/opencodex#408: Also updates Windows elevation probing, owned schtasks /create classification, and related tests.

Suggested reviewers: wibias, lidge-jun

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: Windows scheduler denial classification for localized failures.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/591-windows-schtasks-locale

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

@coderabbitai coderabbitai 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.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs-site/src/content/docs/reference/cli.md`:
- Around line 442-446: Qualify the direct non-elevated Windows install guidance
in docs-site/src/content/docs/reference/cli.md:442-446 so UAC recovery and
language-independent guidance apply only when the owned opencodex-proxy command
and a confirmed non-elevated token are detected; otherwise state that the
original scheduler error is retained. Apply the equivalent translated
clarification in docs-site/src/content/docs/ja/reference/cli.md:363-367,
docs-site/src/content/docs/ko/reference/cli.md:382-386,
docs-site/src/content/docs/ru/reference/cli.md:404-408, and
docs-site/src/content/docs/zh-cn/reference/cli.md:366-369.

In `@src/lib/windows-elevation.ts`:
- Around line 322-326: Update isWindowsSchtasksAccessDeniedError so the
recognized-text branch also requires isOwnedSchedulerCreate(args) before
returning true; retain the scheduler-exit-status and elevation checks for owned
tasks. Preserve generic access-denied text for non-retry user-facing formatting,
and add a regression test covering a foreign /create command targeting
someone-elses-task with “Access is denied.”.
- Around line 49-54: Update the IsUserAnAdmin declaration and wrapper in the
Windows elevation binding: change its FFI return type from "bool" to "i32", and
have isUserAnAdminFn return whether lib.symbols.IsUserAnAdmin() is nonzero.

In `@tests/windows-elevation.test.ts`:
- Around line 107-139: Extend the locale-independent fallback test around
toWindowsSchtasksError with a confirmed-elevated probe case:
setWindowsElevationProbeForTests(() => true), invoke the owned /create
opencodex-proxy command, and assert reason is "other". Reset the probe to null
in a finally block, matching the existing false and null cases.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 64e18d80-28e2-4207-9fd7-44155c0706cb

📥 Commits

Reviewing files that changed from the base of the PR and between c7e48fb and cdc16e5.

📒 Files selected for processing (8)
  • docs-site/src/content/docs/ja/reference/cli.md
  • docs-site/src/content/docs/ko/reference/cli.md
  • docs-site/src/content/docs/reference/cli.md
  • docs-site/src/content/docs/ru/reference/cli.md
  • docs-site/src/content/docs/zh-cn/reference/cli.md
  • src/lib/windows-elevation.ts
  • structure/05_gui-and-management-api.md
  • tests/windows-elevation.test.ts

Comment thread docs-site/src/content/docs/reference/cli.md Outdated
Comment thread src/lib/windows-elevation.ts Outdated
Comment thread src/lib/windows-elevation.ts Outdated
Comment thread tests/windows-elevation.test.ts

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs-site/src/content/docs/reference/cli.md`:
- Around line 442-447: Clarify the documentation so fail-closed behavior for an
unavailable elevation probe applies only to the status-1, unrecognized-text
fallback; preserve the existing localized access-denied text-detection path.
Make this correction in docs-site/src/content/docs/reference/cli.md lines
442-447, docs-site/src/content/docs/ja/reference/cli.md lines 363-368,
docs-site/src/content/docs/ko/reference/cli.md lines 382-386,
docs-site/src/content/docs/ru/reference/cli.md lines 404-409, and
docs-site/src/content/docs/zh-cn/reference/cli.md lines 366-370.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 4b71431c-b352-41a6-9420-b48084264bdb

📥 Commits

Reviewing files that changed from the base of the PR and between cdc16e5 and b9ae659.

📒 Files selected for processing (7)
  • docs-site/src/content/docs/ja/reference/cli.md
  • docs-site/src/content/docs/ko/reference/cli.md
  • docs-site/src/content/docs/reference/cli.md
  • docs-site/src/content/docs/ru/reference/cli.md
  • docs-site/src/content/docs/zh-cn/reference/cli.md
  • src/lib/windows-elevation.ts
  • tests/windows-elevation.test.ts

Comment thread docs-site/src/content/docs/reference/cli.md Outdated
@Ingwannu

Copy link
Copy Markdown
Owner Author

All automated checks are now green, including the 10m45s windows-latest runtime suite. The four CodeRabbit findings were verified, addressed in b9ae6592, replied to inline, and resolved. The PR is MERGEABLE/CLEAN and ready for the requested human security review from @lidge-jun or @Wibias.

I am not self-approving or self-merging this UAC-boundary change. After approval, the merger must also satisfy the documented dev2-go carry/port requirement in the PR body.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs-site/src/content/docs/reference/cli.md`:
- Around line 442-448: Update the UAC-recovery documentation in
docs-site/src/content/docs/reference/cli.md lines 442-448 to state that the
fixed task-create command requires the exact /create command, expected XML path,
and /f checks, in addition to the owned task name, exit status 1, and confirmed
non-elevated token. Translate and apply the same complete predicate in
docs-site/src/content/docs/ja/reference/cli.md lines 363-370,
docs-site/src/content/docs/ko/reference/cli.md lines 382-388,
docs-site/src/content/docs/ru/reference/cli.md lines 404-411, and
docs-site/src/content/docs/zh-cn/reference/cli.md lines 366-371, keeping all
locale guidance aligned with the CLI behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 55f0e29c-d7a7-4cc7-8db8-efe69e7ca937

📥 Commits

Reviewing files that changed from the base of the PR and between b9ae659 and 9338550.

📒 Files selected for processing (5)
  • docs-site/src/content/docs/ja/reference/cli.md
  • docs-site/src/content/docs/ko/reference/cli.md
  • docs-site/src/content/docs/reference/cli.md
  • docs-site/src/content/docs/ru/reference/cli.md
  • docs-site/src/content/docs/zh-cn/reference/cli.md

Comment thread docs-site/src/content/docs/reference/cli.md Outdated
@Ingwannu

Copy link
Copy Markdown
Owner Author

CI update: the docs clarification run had one unrelated Windows timeout in the existing executeArchivedCleanup > satellite-backup write failure leaves every database and rollout unchanged test (5s limit; it completed in 9.75s). The same PR had already passed Windows before that docs-only commit.

I reran the failed Actions run without changing code. The Windows full runtime Test step passed on rerun, followed by GUI tests, privacy scan, lint, build, and smoke checks; the entire cross-platform matrix is green again. This confirms a one-off test timeout rather than a #591 regression.

@Ingwannu

Ingwannu commented Jul 29, 2026

Copy link
Copy Markdown
Owner Author

Created the required dev2-go tracking issue: #703. It records why a TypeScript-only carry is insufficient for the in-process Go startup-action path and captures the fixed-command/fail-closed/UAC rollback invariants. This satisfies the durable tracking requirement while #698 awaits non-author security review.

@lidge-jun
lidge-jun merged commit 97dfbb2 into dev Jul 29, 2026
11 checks passed
lidge-jun added a commit that referenced this pull request Jul 29, 2026
…ion hardening, Windows scheduler fixes

Promotes dev to main for the v2.7.43 quick-fix release:
- fix(gui): renew loopback dashboard sessions silently instead of prompting
- PR #705 + follow-up: Claude effort restoration boundary hardening
- PRs #694/#698/#699 and Windows scheduler compare/denial fixes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants