Skip to content

fix(docker): validate HERMES_UID/GID to prevent privilege escalation#35340

Merged
benbarclay merged 1 commit into
mainfrom
salvage-34119
Jun 1, 2026
Merged

fix(docker): validate HERMES_UID/GID to prevent privilege escalation#35340
benbarclay merged 1 commit into
mainfrom
salvage-34119

Conversation

@teknium1

Copy link
Copy Markdown
Contributor

Summary

HERMES_UID=0 (or any non-numeric / out-of-range value) can no longer be passed straight to usermod/groupmod in the Docker stage2 hook — which previously would silently grant the hermes runtime user root (UID 0). UID/GID remap now requires a numeric value in the range 1000–65534.

Root cause: HERMES_UID/HERMES_GID (and their PUID/PGID aliases) were forwarded to usermod -u / groupmod -g with no validation.

Changes

  • docker/stage2-hook.sh: add validate_uid_gid() (POSIX case numeric check + range bounds); gate both the UID and GID remap conditionals on it.

Validation

  • sh -n clean.
  • E2E (function extracted and exercised directly):
    • REJECT: 0, empty, 0; rm -rf /, abc, 999, 65535, -5, 1e4
    • ACCEPT: 1000, 10000 (hermes default), 1001 (typical NAS PUID), 65534

The 1000 floor blocks UID 0 (the goal) while leaving the default UID 10000 and conventional NAS PUID/PGID values (≥1000) working.

Salvaged from #34119 by @sprmn24 — authorship preserved. Conflict resolved against the PUID/PGID alias block added to main after the PR was opened; validation applied to both UID and GID conditionals.

Infographic

uid-gid-validation

@github-actions

Copy link
Copy Markdown
Contributor

🔎 Lint report: salvage-34119 vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 9507 on HEAD, 9507 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 4931 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

@alt-glitch alt-glitch added type/security Security vulnerability or hardening P1 High — major feature broken, no workaround area/docker Docker image, Compose, packaging labels May 30, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Salvage of #34119. Note: conflicts with closed #35078 which wanted to support UID=0 instead of blocking it. This PR takes the security-hardening approach (block UID 0).

@teknium1 teknium1 requested a review from benbarclay May 30, 2026 13:25

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

Code Review Summary: PR #35340

Verdict: APPROVE

Overall: Clean, focused security fix. 10 lines added, zero bugs. This is exactly the right approach — validate at the entry point rather than downstream.

✅ Looks Good

  • POSIX case validation — portable across sh implementations, correctly handles: empty string, non-numeric, negative numbers, shell injection attempts (0; rm -rf /), and out-of-range values
  • 1000–65534 range — blocks UID 0 (root) entirely, allows the hermes default (10000), common NAS PUID values (>=1000), and nobody (65534). The 65534 ceiling also blocks 65535 ((unsigned short)-1 sentinel)
  • && chaining — if validate_uid_gid fails, the remap is silently skipped (better than failing loudly for backward compatibility)
  • PUID/PGID aliases — naturally covered since they resolve to HERMES_UID/HERMES_GID before the validation check
  • sh -n clean — syntax verified

💡 Suggestion (for follow-up, not blocking)

  • Consider logging when validation rejects a value (e.g., echo "[stage2] Warning: invalid HERMES_UID=$HERMES_UID -- must be numeric 1000-65534; skipping UID remap"). Currently a rejected value is silently ignored, which could be confusing for users who set HERMES_UID=0 expecting it to work.

Testing Completeness

The PR body lists thorough E2E validation against a variety of inputs:

  • REJECTED: 0, empty, 0; rm -rf /, abc, 999, 65535, -5, 1e4
  • ACCEPTED: 1000, 10000, 1001, 65534

All correct. The 1e4 rejection is a nice catch — scientific notation passes *[!0-9]* but usermod would fail.


Reviewed by Hermes Agent

@tonydwb

tonydwb commented May 30, 2026

Copy link
Copy Markdown

Code Review Summary: PR #35340

Verdict: APPROVE

Overall: Clean, focused security fix. 10 lines added, zero bugs. This is exactly the right approach — validate at the entry point rather than downstream.

✅ Looks Good

  • POSIX case validation — portable across sh implementations, correctly handles: empty string, non-numeric, negative numbers, shell injection attempts (0; rm -rf /), and out-of-range values
  • 1000–65534 range — blocks UID 0 (root) entirely, allows the hermes default (10000), common NAS PUID values (>=1000), and nobody (65534). The 65534 ceiling also blocks 65535 ((unsigned short)-1 sentinel)
  • && chaining — if validate_uid_gid fails, the remap is silently skipped (better than failing loudly for backward compatibility)
  • PUID/PGID aliases — naturally covered since they resolve to HERMES_UID/HERMES_GID before the validation check
  • sh -n clean — syntax verified

💡 Suggestion (for follow-up, not blocking)

  • Consider logging when validation rejects a value (e.g., echo "[stage2] Warning: invalid HERMES_UID=$HERMES_UID -- must be numeric 1000-65534; skipping UID remap"). Currently a rejected value is silently ignored, which could be confusing for users who set HERMES_UID=0 expecting it to work.

Testing Completeness

The PR body lists thorough E2E validation against a variety of inputs:

  • REJECTED: 0, empty, 0; rm -rf /, abc, 999, 65535, -5, 1e4
  • ACCEPTED: 1000, 10000, 1001, 65534

All correct. The 1e4 rejection is a nice catch — scientific notation passes *[!0-9]* but usermod would fail.


Reviewed by Hermes Agent

@benbarclay benbarclay merged commit 758454d into main Jun 1, 2026
25 checks passed
@benbarclay benbarclay deleted the salvage-34119 branch June 1, 2026 01:46
JoeKowal pushed a commit to JoeKowal/hermes-agent that referenced this pull request Jun 4, 2026
…in stage2-hook (NousResearch#35340)

Co-authored-by: sprmn24 <oncuevtv@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/docker Docker image, Compose, packaging P1 High — major feature broken, no workaround type/security Security vulnerability or hardening

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants