Skip to content

[WIP] Add OneShot secure file upload architecture - #1

Closed
MinecraftFuns with Copilot wants to merge 1 commit into
mainfrom
copilot/implement-oneshot-secure-upload
Closed

[WIP] Add OneShot secure file upload architecture#1
MinecraftFuns with Copilot wants to merge 1 commit into
mainfrom
copilot/implement-oneshot-secure-upload

Conversation

Copilot AI commented Mar 24, 2026

Copy link
Copy Markdown
Contributor

Thanks for asking me to work on this. I will get started on it and keep this PR's description up to date as I form a plan and make progress.

Original prompt

System Role: You are an Autonomous Lead Security Engineer. Your objective is to scaffold a new application using the h4ckath0n full-stack template and implement the "OneShot" secure file upload architecture.

Execution Environment: You are running in an isolated runner. You must execute shell commands to scaffold the project, write the files to disk, run the tests to verify your implementation, and prepare the final state for a Pull Request.


Phase 0: Project Scaffolding

Execute the following commands in your workspace terminal to scaffold the base project. Because you are already in a git repository, skip the inner git initialization.

npx h4ckath0n@latest oneshot-app --no-git
cd oneshot-app

Note: All file paths in the subsequent phases are relative to the newly created oneshot-app/ directory.


Phase 1: Core ID and Database Modifications

1A. Update ID Generation (api/app/rng.py or equivalent scaffold file)
Extend the custom base32 ID generation. DO NOT use the Python uuid module.

  • Implement new_file_id() -> str: Base32 string, exactly 32 chars, starts with f (e.g., s = random_base32(); return "f" + s[1:]).
  • Implement new_oneshot_token_id() -> str: Base32 string, exactly 32 chars, starts with t.
  • Implement validators: is_file_id(value) and is_oneshot_token_id(value).

1B. Create Database Schema (api/app/uploads/models.py)
Use SQLAlchemy 2.0 syntax (Mapped, mapped_column). Write the following models:

  • OneShotToken:
    • id: Mapped[str] = mapped_column(String(32), primary_key=True, default=new_oneshot_token_id)
    • is_used: Mapped[bool] = mapped_column(default=False)
    • created_by_id: Mapped[str] = mapped_column(ForeignKey("users.id"))
    • target_email: Mapped[str | None] = mapped_column(String, nullable=True)
  • FileMetadata:
    • id: Mapped[str] = mapped_column(String(32), primary_key=True, default=new_file_id)
    • original_filename: Mapped[str] = mapped_column(String)
    • mime_type: Mapped[str] = mapped_column(String)
    • size_bytes: Mapped[int] = mapped_column(Integer)
    • token_id: Mapped[str] = mapped_column(ForeignKey("oneshot_tokens.id"), unique=True)

Phase 2: Backend API Implementation

Modify or create api/app/uploads/router.py to implement these strict security requirements:

A. Admin Token Generation (POST /api/admin/oneshot-tokens):

  • Require an Admin user dependency (disable public user registration in the scaffold's auth module).
  • Accept optional target_email. Insert a OneShotToken.
  • If target_email is provided, trigger a background email task with link https://<domain>/oneshot#token=<token_id>. Otherwise, return the link in the JSON.

B. Public File Upload Route (POST /api/oneshot/upload):

  • Security Check: Extract the token ONLY from the Authorization: Bearer <token_id> header.
  • Atomic Lock (CRITICAL): Use an atomic UPDATE with a RETURNING clause to prevent double-click race conditions:
    stmt = update(OneShotToken).where(
        OneShotToken.id == extracted_token_id, OneShotToken.is_used == False
    ).values(is_used=True).returning(OneShotToken.id)
    If result.first() is empty, raise a 401.
  • Storage: Save to LOCAL_UPLOAD_DIR / new_file_id(). No extensions. Chunk the write (1MB chunks).
  • Metadata: Insert the FileMetadata row.

Phase 3: Frontend Implementation

Modify the React application in web/src/.

A. Public Upload View (web/src/pages/OneShotUpload.tsx):

  • Extract the token client-side from window.location.hash (#token=...). Do NOT send the token in the initial page request.
  • Implement a State Machine: IDLE | UPLOADING | SUCCESS | ERROR_INVALID_TOKEN | ERROR_UPLOAD_FAILED.
  • Submit FormData using fetch/axios, passing the extracted token in the Authorization: Bearer <token> header.
  • Show an explicit "Link Expired" UI if a 401 is returned.

Phase 4: TDD & Self-Correction

Before finalizing the PR, write the following tests and execute them in your environment to ensure your code works. Fix any failures autonomously.

  1. Backend Tests (api/tests/test_oneshot.py):
    • Write an adversarial test: Call POST /api/oneshot/upload sequentially twice with the same Bearer token; assert the second receives a 401.
    • Assert new_file_id() generates exactly 32 chars starting with f.
  2. Frontend Tests (web/src/pages/__tests__/OneShotUpload.test.tsx):
    • Mount with a mocked window.location.hash and assert the token is correctly passed to the fetch request headers.
  3. CI Pipeline:
    • Append jobs to the generated .github/workflows/ci.yml to run uv run pytest in api/ and npm run test in web/.

⌨️ Start Copilot coding agent tasks without leaving your editor — available in VS Code, Visual Studio, JetBrains IDEs and Eclipse.

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.

2 participants