Skip to content

fix(security): keep model caches owner-only - #64202

Open
mudrii wants to merge 1 commit into
NousResearch:mainfrom
mudrii:fix/model-cache-owner-modes
Open

fix(security): keep model caches owner-only#64202
mudrii wants to merge 1 commit into
NousResearch:mainfrom
mudrii:fix/model-cache-owner-modes

Conversation

@mudrii

@mudrii mudrii commented Jul 14, 2026

Copy link
Copy Markdown

Summary

  • create OpenRouter metadata and model-catalog caches with owner-only 0600 permissions
  • route model-catalog writes through the shared atomic JSON writer
  • verify permissions on POSIX while retaining the existing content assertions on Windows

Why

These caches contain locally selected/provider-derived model metadata and previously inherited the process umask. Explicit owner-only modes align them with Hermes' other private state files.

Verification

  • scripts/run_tests.sh tests/agent/test_model_metadata.py tests/hermes_cli/test_model_catalog.py
  • 150 passed, 0 failed
  • ruff check passed
  • git diff --check passed

Copilot AI review requested due to automatic review settings July 14, 2026 06:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR hardens Hermes’ local on-disk caches for model metadata and the remote model-catalog manifest by ensuring they are written with owner-only permissions (0600) instead of inheriting a potentially permissive process umask. This aligns these caches with other private Hermes state files and reduces the risk of local information disclosure.

Changes:

  • Write the CLI model-catalog cache via the shared atomic_json_write(...) helper and explicitly set mode=0o600.
  • Ensure the OpenRouter model metadata disk cache is saved with mode=0o600.
  • Extend tests to validate 0600 permissions on POSIX platforms (while keeping content assertions on Windows).

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
hermes_cli/model_catalog.py Routes cache writes through atomic_json_write and forces owner-only permissions.
agent/model_metadata.py Sets owner-only permissions for the OpenRouter metadata disk cache.
tests/hermes_cli/test_model_catalog.py Adds a POSIX-only assertion that the model-catalog cache file is written as 0600.
tests/agent/test_model_metadata.py Adds a POSIX-only assertion that the OpenRouter metadata cache file is written as 0600.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +112 to +113
if os.name != "nt":
assert stat.S_IMODE(cache_file.stat().st_mode) == 0o600
Comment on lines +1218 to +1219
if os.name != "nt":
assert stat.S_IMODE(cache_path.stat().st_mode) == 0o600
@alt-glitch alt-glitch added type/security Security vulnerability or hardening comp/cli CLI entry point, hermes_cli/, setup wizard comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint area/config Config system, migrations, profiles P3 Low — cosmetic, nice to have labels Jul 14, 2026

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for hardening the two targeted cache writers. The proposed atomic_json_write(..., mode=0o600) mechanism is appropriate: utils.py:175-196 sets the temporary-file mode before replacement on POSIX and reapplies it to the resolved target.

Problems

  • The hardening is incomplete for sibling persisted model caches. agent/models_dev.py:235, hermes_cli/models.py:2727, and hermes_cli/models.py:4011 use atomic_json_write without mode=0o600; hermes_cli/models.py:861-884 writes the Nous recommended-model cache through a default-mode temporary file. Those paths remain umask-dependent.

Suggested changes

  • Apply the same owner-only mode to those writers, convert the Nous cache write to the shared atomic JSON helper, and add POSIX permission assertions for each persisted model-cache path.

This is an automated hermes-sweeper review.

json.dump(data, fh, indent=2)
fh.write("\n")
atomic_replace(tmp, path)
atomic_json_write(path, data, indent=2, mode=0o600)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please extend this owner-only write policy to the sibling persisted model caches: agent/models_dev.py:235, hermes_cli/models.py:2727, hermes_cli/models.py:4011, and the direct temporary-file write at hermes_cli/models.py:861-884 remain umask-dependent.

@teknium1 teknium1 added sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 16, 2026
@GottZ

GottZ commented Aug 3, 2026

Copy link
Copy Markdown

This was generated by AI during triage.

Summary

Ten PRs were checked across the model-catalog cluster. #54737, #54793, and #54840 are the recorded best fixes for bounded provider/generic, manifest, and broad live-catalog reads respectively; #64202 instead hardens two persisted cache writers, while the other PRs address overlapping provider reads or separate validation, configuration, discovery, and picker behavior.

Related pull requests

Duplicates

Closed #54765 and #56495 substantially duplicate the ProviderProfile.fetch_models() portion of #54737; #56495 was explicitly closed as a duplicate of #54737, while #54765 was closed in favor of #42930. #54737 and #54840 overlap at the generic probe but are not full duplicates, and #54793 covers the complementary manifest path.

Suggested consolidation

Keep #64202 open with a salvage path: preserve its two owner-only atomic cache writes, then address the contributor keep-open review by extending 0600 handling and POSIX permission tests to the four named sibling cache writers. Keep recorded best-fix PRs #54737, #54793, and #54840 open on their separate conflict-aware salvage paths; #54765 and #56495 are already-closed provider-side duplicates and should remain closed.

Cross-PR triage: Reviewed 10 pull requests and 4 issues in this complex. Each diff was read against this issue; Assessment working set: 74 kB of PR diffs, 26 kB of issue/PR text, 22 kB of discussion (28 comments), 16 verify verdicts. verdicts reflect diff content, not PR titles. Part of an automated triage batch.

@egilewski

Copy link
Copy Markdown
Contributor

suggesting changes

The cache hardening is incomplete: fresh legacy cache files are accepted without an owner-only permission check. A pre-existing broad-permission catalog or OpenRouter metadata cache can remain readable and replaceable through fresh, stale/offline, or failed-refresh paths, allowing local peers to influence model metadata and selection.

  • [P3] Fresh legacy cache reads bypass the owner-only invariant
    Both readers return valid legacy files before repairing or verifying permissions. Ensure every disk-cache read enforces owner-only access (or fails closed), and cover fresh, stale/offline, and failed-refresh cases with equivalent tests.

Security evidence:

  • trust boundary: Remote catalog/metadata and on-disk caches are untrusted; local-user isolation requires owner-only cache access.
  • source/sink/invariant: Successful writes pass mode=0o600, but fresh-read branches return existing files without a permission check or repair.
  • current-main reproduction: The pre-fix writers preserved default broad permissions, so an existing cache could remain group/world-readable.
  • PR-head or patch-replay validation: Refresh writes now produce 0600 on POSIX, while fresh legacy reads still leave broad modes unchanged.
  • positive/negative cases: New or refreshed files are owner-only; valid pre-existing broad-permission catalog and metadata files are still served unchanged.
  • residual bypass search: Both cache readers retain the same early-return bypass; unrelated cache files are outside this finding.
  • reviewer validation: Source inspection and focused tests confirmed the write hardening and the fresh-read bypass.

Review setup: I reviewed a run-owned local rebase or patch replay against current GitHub main because the submitted branch is stale or conflicted; this does not mean the submitted branch itself merges cleanly.

Not checked:

  • Windows ACL behavior
  • CodeRabbit review

Signed: GPT-5.6-luna-max in Codex

@Enough1122

Copy link
Copy Markdown
Contributor

AI code review — automated review for reference, author can ignore or act on any point.

fix(security): keep model caches owner-only

  1. The permission tightening only applies at rewrite time: cache files already on disk with looser perms (created by previous versions under a permissive umask, e.g. 0o644) stay loose until the next successful refresh. Consider a chmod-on-load pass (or a startup sweep) so pre-existing caches are remediated immediately after upgrade.
  2. Windows: mode=0o600 is effectively a no-op (no fchmod, and the tests correctly skip the assertion on nt). mkstemp restricts the temp file, but the final file's ACL on Windows is not governed by this change — the hardening is POSIX-only. Worth documenting that boundary so the security claim is accurate per platform.
  3. Minor: the two cache writers now both use atomic_json_write(..., mode=0o600) (agent/model_metadata.py and hermes_cli/model_catalog.py) — a shared helper would keep the two paths in lockstep (format, mode, atomicity) if either is touched later.

@alt-glitch alt-glitch removed sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows labels Aug 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/config Config system, migrations, profiles comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/cli CLI entry point, hermes_cli/, setup wizard P3 Low — cosmetic, nice to have sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades type/security Security vulnerability or hardening

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants