Security: Fix authentication bypass when ADMIN_KEY is unset and secure memory clear endpoint#5012
Security: Fix authentication bypass when ADMIN_KEY is unset and secure memory clear endpoint#5012Stevenn28 wants to merge 2 commits into
Conversation
|
Welcome to RustChain! Thanks for your first pull request. Before we review, please make sure:
Bounty tiers: Micro (1-10 RTC) | Standard (20-50) | Major (75-100) | Critical (100-150) A maintainer will review your PR soon. Thanks for contributing! |
god-ts
left a comment
There was a problem hiding this comment.
I am requesting changes because the patch improves a few paths, but it still leaves parts of the claimed security issues open and introduces an unsafe admin-key transport.
Findings:
-
contributor_registry.py:20,payout_ledger.py:27, andbounties/issue-2285/src/memory_routes.py:46acceptadmin_keyfrom the URL query string. That means the new admin secret can be written into access logs, browser history, reverse-proxy logs, referrers, and monitoring traces. Since this PR is hardening admin-only destructive/financial routes, please require the key only in a header such asX-Admin-Key/X-API-Keyand avoid query-string credentials. -
payout_ledger.py:196-210still exposes the payout ledger read endpoints without any authentication. Issue #4902 explicitly listsGET /api/ledgerandGET /api/ledger/<id>as part of the data leak because they reveal contributors, wallet addresses, amounts, statuses, PR URLs, tx hashes, and notes. This PR protects create/update, but if it closes #4902 it should either protect those reads too or deliberately redact sensitive fields from public responses. -
contributor_registry.py:152-174still lets anyone register anygithub_usernamewith any wallet, and the form still has no CSRF protection. Adding wallet prefix validation and protecting/approve/<username>helps the final approval step, but it does not close the identity-claim / queue-poisoning part of #4911. At minimum, please do not mark #4911 closed unless registration ownership/CSRF/rate-limit coverage is added or the issue is narrowed.
Validation performed:
python3 -m py_compile contributor_registry.py payout_ledger.py bounties/issue-2285/src/memory_routes.py node/machine_passport_api.py
# passed
git diff --check origin/main...HEAD
# failed: trailing whitespace in contributor_registry.py and node/machine_passport_api.py
python3 tools/bcos_spdx_check.py --base-ref origin/main
# BCOS SPDX check: OK
Recommended fix: remove query-parameter admin auth, add focused Flask client regressions for missing/invalid/valid headers on the four protected mutating routes, decide whether payout ledger reads are admin-only or redacted, and either implement GitHub ownership/CSRF/rate-limit protection for registration or stop closing #4911 from this PR.
Code Review: Fix authentication bypass when ADMIN_KEY unsetSummaryFixes the default-allow pattern in memory_routes and contributor_registry (same bugs as our #4882 and #4912). Security Issue FoundThe admin check uses timing-unsafe comparison: if req_key != ADMIN_KEY: # ← TIMING-UNSAFE!
abort(401)This should be This is the same class of bug as #5000 (bridge API timing attack). Positive
SuggestionReplace import hmac
if not hmac.compare_digest(req_key or "", ADMIN_KEY):
abort(401)Needs fix before merge — timing-unsafe comparison is a security vulnerability in a security fix. Review quality: Security-focused review (20-25 RTC) |
loganoe
left a comment
There was a problem hiding this comment.
I agree this needs changes. In addition to the existing security review concerns, the affected test suites are red because the PR changes auth behavior without updating the corresponding expectations/setup.
Findings:
bounties/issue-2285/tests/test_memory_routes.py::MemoryRoutesTestCase::test_clear_memorystill callsDELETE /api/memory/clear?agent_id=test-agentwithout an admin header and now gets 403 instead of the expected 200. This PR should add missing/wrong/correct admin-key route coverage for the destructive clear endpoint.tests/test_contributor_registry.py::TestRegisterRoute::test_register_duplicate_usernamepostsRTC0dup, which the new wallet validation rejects before duplicate handling, so the test no longer verifies duplicates.TestApproveRoute::test_approve_pending_contributoralso still assumes unauthenticated approval succeeds.node/tests/test_machine_passport.pynow has three failures: create-without-admin expects 201 but gets 401, and the update auth tests get 404 because the setup POST no longer creates the passport without an admin key. The tests need to seed/create passports under the new default-deny contract before testing updates.git diff --check origin/main...HEADfails on trailing whitespace incontributor_registry.pyandnode/machine_passport_api.py.
Validation run:
PYTHONPATH=bounties/issue-2285/src ... pytest bounties/issue-2285/tests/test_memory_routes.py -q-> 1 failed, 29 passed, 11 subtests passed... pytest tests/test_contributor_registry.py tests/test_payout_ledger_migration.py tests/test_machine_passport_event_json_validation.py node/tests/test_machine_passport.py -q-> 5 failed, 40 passedpython3 -m py_compile contributor_registry.py payout_ledger.py bounties/issue-2285/src/memory_routes.py node/machine_passport_api.py-> passeduv run --no-project --with ruff ruff check contributor_registry.py payout_ledger.py bounties/issue-2285/src/memory_routes.py node/machine_passport_api.py --select E9,F821,F811,F841 --output-format=concise-> passedpython3 tools/bcos_spdx_check.py --base-ref origin/main-> OK
Please update the affected tests for the new auth contract and clean up the diff-check failures before merge.
TJCurnutte
left a comment
There was a problem hiding this comment.
I’m requesting changes because the new admin guards still accept the admin secret in the URL on several mutating routes.
Validation I ran:
git diff --check origin/main...HEAD -- bounties/issue-2285/src/memory_routes.py contributor_registry.py node/machine_passport_api.py payout_ledger.pyfailed on trailing whitespace atcontributor_registry.py:162,node/machine_passport_api.py:212, andnode/machine_passport_api.py:313.python3 -B -m py_compile bounties/issue-2285/src/memory_routes.py contributor_registry.py node/machine_passport_api.py payout_ledger.pypassed.- Focused Flask probe against
payout_ledger.register_ledger_routes()withADMIN_KEY=sekrit:POST /api/ledgerwith no key ->401POST /api/ledger?admin_key=sekrit->201 {'status': 'queued'}POST /api/ledgerwithX-Admin-Key: sekrit->201 {'status': 'queued'}
- Static check found
request.args.get("admin_key")inbounties/issue-2285/src/memory_routes.py,contributor_registry.py, andpayout_ledger.py; those guards use plain!=.node/machine_passport_api.pyis the only touched file here usinghmac.compare_digestand header-only admin auth.
The patch does close the fully unauthenticated path when ADMIN_KEY is unset, but ?admin_key= on POST/PATCH/DELETE/admin routes still exposes the admin credential through access logs, browser history, proxy logs, and referrers. Please remove the query-string fallback, require a header for the secret, and use constant-time comparison across the new admin guards before merging.
himanalot
left a comment
There was a problem hiding this comment.
Requesting changes.
The machine-passport default-deny change is good, but the new admin_required wrappers added in this PR accept request.args.get("admin_key") and compare with req_key != ADMIN_KEY. That puts admin secrets in URLs/logs/referrers and browser history, and the new comparisons are not constant-time.
This affects the newly protected memory clear route and the unrelated contributor/payout mutations included in the same PR. Please make the new admin checks header-only and use hmac.compare_digest (or reuse an existing shared admin-auth helper) before merging.
|
Paid 10 RTC of 40 RTC cluster (PRs #5011 #5012 #5014 #5080). tx_hash: 0ff9c8d19252e2edca197859e66845ad | pending #1510 | confirms in 24h Closing as merge-conflict. All 4 PRs add overlapping To unblock: rebase a single consolidated PR off latest main with all 4 hardenings in one diff, and we'll merge it as a follow-on (no additional bounty — the work is already paid). — triage 2026-05-14 |
HCIE2054
left a comment
There was a problem hiding this comment.
LGTM! Thanks for contributing!
Description\n\nCloses #4880\nCloses #4878\n\nThis PR fixes two HIGH severity security vulnerabilities related to the
ADMIN_KEY:\n\n1. Memory API Clear Endpoint (#4880): Added the@admin_requireddecorator to theDELETE /api/memory/clearendpoint. Previously, this endpoint had no authentication, allowing anyone to completely wipe an agent's memory. Now it requires a validX-Admin-Key.\n\n2. Machine Passport API Authentication Bypass (#4878): Changed the admin key check to default-deny. Previously, if theADMIN_KEYenvironment variable was not set, the authentication check was skipped entirely, leaving admin endpoints wide open. It now explicitly rejects requests if theADMIN_KEYis not configured.