Security audit: MCP server secret management#216
Conversation
|
@claude code review |
|
Claude finished @RafaelPo's task in 3m 13s —— View job Code Review: Security Audit Report
Overall AssessmentThorough audit — all 11 findings verified against the current codebase. Line numbers, code snippets, and severity context are accurate. One factual error in the positive findings table needs correction. Factual Error: OIDC Claim is WrongThe positive findings table states:
This is incorrect. The workflow at - uses: google-github-actions/auth@v2
with:
credentials_json: ${{ secrets.GCP_CREDENTIALS_GLOBAL }}This is a long-lived JSON service account key stored in GitHub Secrets, not OIDC Workload Identity Federation. OIDC would use Finding Verification Summary
Positive findings table: 17 of 18 verified correct. The OIDC claim (row 18) is wrong as described above. Severity Calibration NotesH-1 (HKDF salt=None) — The severity is defensible but worth nuancing. Per RFC 5869, H-1 recommended fix — The Minor Nits in the Document
Good Catch: Something the Audit Got Right That's Easy to MissThe H-2 finding correctly noting that SummaryThe audit is high quality and actionable. The one required fix is correcting the OIDC positive finding. The H-1 recommended fix should include a migration note. All vulnerability findings are real and accurately described. |
Comprehensive defensive audit of the MCP server covering source code, deployment config, CI/CD workflows, and git history. Identifies 2 High, 5 Medium, and 4 Low severity findings with recommended fixes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…length H-2: PendingAuth (containing PKCE code verifier) is now encrypted with encrypt_value() before storage in Redis, matching the pattern already used for auth codes and refresh tokens. Decrypt on retrieval. M-4: UPLOAD_SECRET must now be at least 32 characters in HTTP mode, preventing weak secrets from passing the empty-string check. Also addresses code review feedback on the audit report: - Fix incorrect OIDC claim (actually uses credentials_json, not OIDC) - Add second H-2 location (handle_start re-store at auth.py:370-373) - Add migration warning to H-1 recommended fix - Soften HKDF salt impact per RFC 5869 (domain separation, not brute-force) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
962c426 to
67d12bb
Compare
| if pending_data is None: | ||
| if pending_data_encrypted is None: | ||
| raise HTTPException(status_code=400, detail="Invalid or expired state") | ||
| pending_data = decrypt_value(pending_data_encrypted) |
There was a problem hiding this comment.
Bug: The code attempts to decrypt PendingAuth data from Redis without handling cases where the data might be unencrypted from a previous version, causing an unhandled exception.
Severity: HIGH
Suggested Fix
Wrap the decryption call in a try...except InvalidToken block. In the except block, attempt to use the data as plaintext to maintain backward compatibility for entries created before the deployment. This ensures a smooth transition for in-flight authentication flows.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.
Location: everyrow-mcp/src/everyrow_mcp/auth.py#L284
Potential issue: The introduction of encryption for `PendingAuth` data lacks a backward
compatibility or migration strategy. During a deployment, if an authentication flow was
initiated before the update, an unencrypted `PendingAuth` entry will exist in Redis.
When the new code in `_validate_auth_request()` tries to process this entry, the call to
`decrypt_value(pending_data_encrypted)` will fail. The underlying `Fernet.decrypt()`
function will raise an `InvalidToken` exception on the plaintext data. Since this
exception is not caught, it will result in a 500 error, breaking the authentication flow
for any user caught in this transition period. The issue is temporary, as the affected
Redis keys have a 10-minute TTL.
Did we get this right? 👍 / 👎 to inform future reviews.
Summary
Key findings
salt=None— deterministic across environmentsPendingAuth(PKCE verifier) stored unencrypted in Redis while auth codes/refresh tokens are encryptedTest plan
🤖 Generated with Claude Code