fix: sign /upload with empty body — API skips body parsing for streams

The upload endpoint streams the request body in chunks without buffering,
so raw_body stays empty on the server side. The HMAC must sign against an
empty body string to match. _sign_from_disk (which hashed file contents)
produced signatures the server could never verify — every upload got 401.
This commit is contained in:
russell@unturf.com 2026-02-19 20:00:23 -05:00
parent c3072c2a6e
commit 94166be988

View file

@ -41,32 +41,21 @@ def _sign_body(secret_key, timestamp, method, path, body_bytes):
return h.hexdigest()
def _sign_from_disk(secret_key, timestamp, method, path, file_path):
"""Incremental HMAC-SHA256 matching un._sign_request but reading from disk.
Produces: HMAC(key, "ts:METHOD:path:" + <file contents>)
"""
h = hmac.new(secret_key.encode(), digestmod=hashlib.sha256)
h.update(f"{timestamp}:{method}:{path}:".encode())
with open(file_path, "rb") as f:
while True:
chunk = f.read(_READ_CHUNK)
if not chunk:
break
h.update(chunk)
return h.hexdigest()
def _upload_file(file_path, filename, public_key, secret_key):
"""Upload a file via POST /upload (streaming, constant memory).
Returns the upload_id string.
Note: the API skips body parsing for /upload (body streams in chunks),
so the HMAC signs against an empty body not the file content.
"""
timestamp = int(time.time())
method = "POST"
path = "/upload"
signature = _sign_from_disk(secret_key, timestamp, method, path, file_path)
# API doesn't buffer upload bodies for HMAC — sign with empty body
signature = _sign_body(secret_key, timestamp, method, path, "")
headers = {
"Authorization": f"Bearer {public_key}",