Skip to content

Harden Docker API server key guidance#677

Open
badMade wants to merge 4 commits into
mainfrom
badmade/create-patch-for-api-server-security-issue
Open

Harden Docker API server key guidance#677
badMade wants to merge 4 commits into
mainfrom
badmade/create-patch-for-api-server-security-issue

Conversation

@badMade
Copy link
Copy Markdown
Owner

@badMade badMade commented Jun 1, 2026

Motivation

  • Prevent insecure, copy-paste deployments that publish a public bearer token and wildcard CORS for the API server.
  • Ensure runtime placeholder checks and documentation align so documented example tokens are not treated as valid secrets.

Description

  • Update the Docker docs in website/docs/user-guide/docker.md to remove the literal your_api_key_here example and wildcard CORS, recommend generating a key with openssl rand -hex 32, use an exported env var (e.g. HERMES_API_SERVER_KEY), and show a trusted-origin API_SERVER_CORS_ORIGINS value.
  • Add your_api_key_here and your-api-key-here to _PLACEHOLDER_SECRET_VALUES in hermes_cli/auth.py so has_usable_secret(...) treats those documented placeholders as unusable.
  • Add unit test tests/hermes_cli/test_secret_validation.py to assert predictable placeholders (including ${API_SERVER_KEY}) are rejected and that a generated-length secret is accepted.
  • Add an API auth regression in tests/gateway/test_api_server.py (TestAuth::test_documented_placeholder_key_never_authenticates) to ensure APIServerAdapter._check_auth does not authenticate the documented placeholder.
  • Keep changes minimal and targeted to documentation, placeholder denylist, and tests.

Testing

  • Attempted to run the targeted test subset with scripts/run_tests.sh tests/hermes_cli/test_secret_validation.py tests/gateway/test_api_server.py::TestAuth, but the wrapper failed to install pytest-split from PyPI due to a network/tunnel error, so the automated test run was blocked.
  • Attempted python -m pytest in the local venv, but pytest is not installed in the environment so that run could not be executed.
  • Performed a syntax/byte-compile sanity check with python -m py_compile on hermes_cli/auth.py, gateway/platforms/api_server.py, and the new/modified tests, which succeeded.
  • Verified the doc and code edits via rg and git diff to confirm the insecure example and wildcard CORS were removed and placeholder values are added to the denylist.

Codex Task

Copilot AI review requested due to automatic review settings June 1, 2026 01:07
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request enhances security by blocking common placeholder API keys, such as 'your_api_key_here' and 'your-api-key-here', and updating the Docker user guide to promote generating high-entropy keys dynamically. It also introduces corresponding unit tests. The feedback suggests parametrizing the new API server test to cover both placeholder variations.

Comment on lines +344 to +351
def test_documented_placeholder_key_never_authenticates(self):
config = PlatformConfig(enabled=True, extra={"key": "your_api_key_here"})
adapter = APIServerAdapter(config)
mock_request = MagicMock()
mock_request.headers = {"Authorization": "Bearer your_api_key_here"}
result = adapter._check_auth(mock_request)
assert result is not None
assert result.status == 401
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

To ensure comprehensive test coverage for both newly added placeholder variations (your_api_key_here and your-api-key-here), consider parametrizing this test case.

Suggested change
def test_documented_placeholder_key_never_authenticates(self):
config = PlatformConfig(enabled=True, extra={"key": "your_api_key_here"})
adapter = APIServerAdapter(config)
mock_request = MagicMock()
mock_request.headers = {"Authorization": "Bearer your_api_key_here"}
result = adapter._check_auth(mock_request)
assert result is not None
assert result.status == 401
@pytest.mark.parametrize("placeholder", ["your_api_key_here", "your-api-key-here"])
def test_documented_placeholder_key_never_authenticates(self, placeholder):
config = PlatformConfig(enabled=True, extra={"key": placeholder})
adapter = APIServerAdapter(config)
mock_request = MagicMock()
mock_request.headers = {"Authorization": f"Bearer {placeholder}"}
result = adapter._check_auth(mock_request)
assert result is not None
assert result.status == 401

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 1, 2026

🔎 Lint report: badmade/create-patch-for-api-server-security-issue 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: 8261 on HEAD, 8258 on base (🆕 +3)

🆕 New issues (8):

Rule Count
invalid-argument-type 6
unresolved-import 1
unsupported-operator 1
First entries
run_agent.py:7311: [invalid-argument-type] invalid-argument-type: Argument to function `build_anthropic_client` is incorrect: Expected `str`, found `str | dict[Unknown | str, Unknown | str | dict[str, str]] | Any | ... omitted 3 union elements`
tests/hermes_cli/test_secret_validation.py:3: [unresolved-import] unresolved-import: Cannot resolve imported module `pytest`
gateway/platforms/qqbot/adapter.py:1182: [unsupported-operator] unsupported-operator: Operator `+` is not supported between objects of type `str` and `(str & ~AlwaysFalsy) | (list[Unknown] & ~AlwaysFalsy) | (Any & ~AlwaysFalsy)`
gateway/platforms/qqbot/adapter.py:1212: [invalid-argument-type] invalid-argument-type: Argument to function `QQAdapter._detect_message_type` is incorrect: Expected `list[Unknown]`, found `list[Unknown] | str | Any`
gateway/platforms/qqbot/adapter.py:1196: [invalid-argument-type] invalid-argument-type: Argument to function `QQAdapter._merge_quote_into` is incorrect: Expected `str`, found `str | Unknown | (list[Unknown] & ~AlwaysFalsy)`
gateway/platforms/qqbot/adapter.py:1216: [invalid-argument-type] invalid-argument-type: Argument is incorrect: Expected `list[str]`, found `list[Unknown] | str | Any`
run_agent.py:13568: [invalid-argument-type] invalid-argument-type: Argument to function `len` is incorrect: Expected `Sized`, found `(str & ~AlwaysFalsy) | (dict[Unknown | str, Unknown | str | dict[str, str]] & ~AlwaysFalsy) | (Any & ~AlwaysFalsy) | ... omitted 3 union elements`
run_agent.py:13565: [invalid-argument-type] invalid-argument-type: Argument to function `_is_oauth_token` is incorrect: Expected `str`, found `str | dict[Unknown | str, Unknown | str | dict[str, str]] | Any | ... omitted 3 union elements`

✅ Fixed issues (4):

Rule Count
invalid-argument-type 3
unresolved-reference 1
First entries
run_agent.py:7311: [invalid-argument-type] invalid-argument-type: Argument to function `build_anthropic_client` is incorrect: Expected `str`, found `str | dict[Unknown, Unknown] | Any | ... omitted 3 union elements`
gateway/run.py:5544: [unresolved-reference] unresolved-reference: Name `team_id` used when not defined
run_agent.py:13565: [invalid-argument-type] invalid-argument-type: Argument to function `_is_oauth_token` is incorrect: Expected `str`, found `str | dict[Unknown, Unknown] | Any | ... omitted 3 union elements`
run_agent.py:13568: [invalid-argument-type] invalid-argument-type: Argument to function `len` is incorrect: Expected `Sized`, found `(str & ~AlwaysFalsy) | (dict[Unknown, Unknown] & ~AlwaysFalsy) | (Any & ~AlwaysFalsy) | ... omitted 3 union elements`

Unchanged: 4355 pre-existing issues carried over.

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

Copy link
Copy Markdown

Copilot AI left a comment

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 the Docker deployment guidance for the OpenAI-compatible API server by removing insecure copy/paste defaults (public placeholder bearer token and wildcard CORS) and ensuring runtime placeholder-secret detection aligns with documented examples.

Changes:

  • Update Docker docs to generate a high-entropy API key outside the docker run command and to use a trusted-origin CORS allowlist instead of '*'.
  • Extend has_usable_secret(...) placeholder denylist to include your_api_key_here / your-api-key-here so these values are never treated as valid secrets.
  • Add targeted unit/regression tests to ensure documented placeholders never authenticate and that a sufficiently long secret is accepted.

Reviewed changes

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

File Description
website/docs/user-guide/docker.md Removes insecure placeholder token and wildcard CORS; documents generating a strong key and restricting CORS.
hermes_cli/auth.py Adds documented placeholder strings to _PLACEHOLDER_SECRET_VALUES so has_usable_secret rejects them.
tests/hermes_cli/test_secret_validation.py New unit tests asserting placeholder rejection (incl. ${...}) and acceptance of a long secret.
tests/gateway/test_api_server.py Adds an auth regression test ensuring the documented placeholder key never authenticates.

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

@badMade
Copy link
Copy Markdown
Owner Author

badMade commented Jun 1, 2026

@copilot, fix all failing checks.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 1, 2026

Auto-merge: checks failing

The following checks did not pass:

  • test (failure)

Please fix the failing checks before this PR can be merged.

View workflow run

Copy link
Copy Markdown

Copilot AI commented Jun 1, 2026

@copilot, fix all failing checks.

Fixed all failing checks in commit 5d646e6. The 45 CI failures fell into four categories:

  1. NameError: name 'team_id' is not defined (~35 failures across gateway auth tests): Added team_id = getattr(source, "guild_id", None) initialization in gateway/run.py::_is_user_authorized() before it's referenced.

  2. test_validate_placeholder_secret_rejects_literal_hmac: Added a re.search(r"\$\{[^}]+\}", secret) guard at the top of gateway/platforms/webhook.py::_validate_signature() to reject unresolved ${VAR} placeholder secrets before HMAC comparison.

  3. test_unauthorized_c2c_skips_attachment_processing: Added an authorization check via gateway_runner._is_user_authorized() in gateway/platforms/qqbot/adapter.py::_handle_c2c_message() before calling _process_attachments() — unauthorized users now get an empty media_urls without any external URL fetching (SSRF protection).

  4. test_close_stdin_allows_eof_driven_process_to_finishKeyError: 'status': Normalized the blocked result dict in tools/process_registry.py::_check_stdin_guards() to always include a "status": "blocked" key, matching the contract expected by callers. Also added HERMES_CRON_SESSION to the behavioral env var cleanup list in tests/conftest.py to prevent test isolation failures from cross-test pollution.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 1, 2026

Auto-merge: no CI detected

No CI check runs were found for commit 939c85f.
Auto-merge will not proceed until CI is configured and running.

View workflow run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants