Skip to content

Comments

Add admin delete submission API endpoint#448

Closed
msaroufim wants to merge 1 commit intomainfrom
admin-delete-submission
Closed

Add admin delete submission API endpoint#448
msaroufim wants to merge 1 commit intomainfrom
admin-delete-submission

Conversation

@msaroufim
Copy link
Member

Summary

  • Adds DELETE /admin/submissions/{submission_id} endpoint protected by ADMIN_API_SECRET env var (shared secret with kernelboard)
  • Kernelboard verifies admin identity via its whitelist before proxying to this endpoint, avoiding duplicating the admin list across repos
  • Reuses existing db.get_submission_by_id() and db.delete_submission() logic

Context

Currently admin submission deletion is only possible via Discord (/admin delete-submission). This enables the same action from the kernelboard web UI. The admin check lives in kernelboard (hardcoded Discord ID whitelist), and this endpoint trusts that check via a shared secret — keeping the admin list in one place.

Note: popcorn-cli already has user-level DELETE /user/submissions/{id} (owner-only). Extending admin delete to CLI would be a natural follow-up.

Deployment

Both kernelboard and kernelbot need the same ADMIN_API_SECRET env var set.

Test plan

  • Verify 403 when X-Admin-Secret header is missing or wrong
  • Verify 404 for non-existent submission ID
  • Verify successful delete removes runs + submission
  • Verify existing DELETE /user/submissions/{id} still works unchanged

Adds DELETE /admin/submissions/{submission_id} protected by a shared
ADMIN_API_SECRET. Kernelboard verifies admin identity via its whitelist
before proxying to this endpoint, so no duplicated admin list needed here.
@msaroufim msaroufim force-pushed the admin-delete-submission branch from 63272f2 to 38c7fa0 Compare February 18, 2026 16:38
@github-actions
Copy link

Coverage report

Click to see where and how coverage changed

FileStatementsMissingCoverageCoverage
(new stmts)
Lines missing
  src/libkernelbot
  utils.py
Project Total  

This report was generated by python-coverage-comment-action

Copy link
Contributor

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 pull request adds an admin submission deletion endpoint intended for use by the kernelboard web UI. The endpoint uses a shared secret authentication mechanism (ADMIN_API_SECRET) rather than the standard admin token authentication used by other admin endpoints. The PR description indicates that kernelboard verifies admin identity via its whitelist before proxying requests to this endpoint.

Changes:

  • Adds DELETE /admin/submissions/{submission_id} endpoint with X-Admin-Secret header authentication
  • Reuses existing db.get_submission_by_id() and db.delete_submission() database methods
  • Implements 404 check before deletion (unlike the existing admin delete endpoint)
Comments suppressed due to low confidence (1)

src/kernelbot/api/main.py:872

  • This new endpoint lacks test coverage. The test file tests/test_admin_api.py includes comprehensive tests for other admin endpoints including the duplicate DELETE /admin/submissions/{submission_id} at line 578. Tests should be added for this new endpoint covering: 1) Missing X-Admin-Secret header (403), 2) Invalid X-Admin-Secret (403), 3) Valid secret with non-existent submission (404), 4) Valid secret with existing submission (successful deletion). The test fixture would need to be updated to set the ADMIN_API_SECRET environment variable.
    x_admin_secret: Optional[str] = Header(None, alias="X-Admin-Secret"),
    db_context=Depends(get_db),
) -> dict:
    """Admin-only: delete any submission by ID, regardless of ownership.

    Protected by a shared secret between kernelboard and kernelbot.
    Kernelboard verifies admin identity (whitelist) before calling this.
    """
    await simple_rate_limit()

    if not env.ADMIN_API_SECRET or x_admin_secret != env.ADMIN_API_SECRET:
        raise HTTPException(status_code=403, detail="Admin access required")

    try:
        with db_context as db:
            submission = db.get_submission_by_id(submission_id)
            if submission is None:
                raise HTTPException(status_code=404, detail="Submission not found")

            db.delete_submission(submission_id)
            return {"message": f"Submission {submission_id} deleted successfully"}
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(
            status_code=500, detail=f"Error deleting submission: {e}"
        ) from e


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

raise HTTPException(status_code=404, detail="Submission not found")

db.delete_submission(submission_id)
return {"message": f"Submission {submission_id} deleted successfully"}
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

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

The response format is inconsistent with the user delete endpoint. The user delete endpoint at line 833 returns {"status": "ok", "submission_id": submission_id} while this admin endpoint returns {"message": f"Submission {submission_id} deleted successfully"}. For consistency and API design coherence, both endpoints should use the same response format since they perform the same operation.

Suggested change
return {"message": f"Submission {submission_id} deleted successfully"}
return {"status": "ok", "submission_id": submission_id}

Copilot uses AI. Check for mistakes.
Comment on lines +843 to +848
x_admin_secret: Optional[str] = Header(None, alias="X-Admin-Secret"),
db_context=Depends(get_db),
) -> dict:
"""Admin-only: delete any submission by ID, regardless of ownership.

Protected by a shared secret between kernelboard and kernelbot.
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

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

This endpoint duplicates the existing DELETE /admin/submissions/{submission_id} endpoint defined at line 578. FastAPI will use the first registered route, making this new endpoint unreachable. The existing endpoint uses the standard require_admin dependency (with Authorization Bearer token), while this one uses a different authentication method (X-Admin-Secret header). Either the existing endpoint should be removed/replaced, or this new endpoint should use a different path (e.g., /admin/submissions/{submission_id}/delete-alt or /kernelboard/admin/submissions/{submission_id}).

Copilot uses AI. Check for mistakes.
raise HTTPException(status_code=500, detail=f"Error deleting submission: {e}") from e


@app.delete("/admin/submissions/{submission_id}")
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

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

The ADMIN_API_SECRET should be accessed through the env module following the codebase convention, not directly via os.environ.get(). Add env.ADMIN_API_SECRET = os.getenv("ADMIN_API_SECRET", "") to src/kernelbot/env.py (similar to line 19 for ADMIN_TOKEN), then use env.ADMIN_API_SECRET here instead of ADMIN_API_SECRET. This maintains consistency with how other environment variables like ADMIN_TOKEN are handled throughout the codebase.

Copilot uses AI. Check for mistakes.
if not env.ADMIN_API_SECRET or x_admin_secret != env.ADMIN_API_SECRET:
raise HTTPException(status_code=403, detail="Admin access required")

try:
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

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

The secret comparison on line 856 is vulnerable to timing attacks. An attacker could use timing measurements to determine the correct secret character by character. Use secrets.compare_digest() for constant-time comparison of secrets to prevent timing attacks. This should be: if not ADMIN_API_SECRET or not secrets.compare_digest(x_admin_secret or "", ADMIN_API_SECRET):

Copilot uses AI. Check for mistakes.
@msaroufim
Copy link
Member Author

No kernelbot changes needed — the existing DELETE /admin/submissions/{id} endpoint (with Authorization: Bearer ADMIN_TOKEN) already handles this. Updated kernelboard PR to use it directly.

@msaroufim msaroufim closed this Feb 18, 2026
@msaroufim msaroufim deleted the admin-delete-submission branch February 18, 2026 18:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant