Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,13 @@
**Vulnerability:** The `_safe_upload_filename` function used `filename.replace`, `PurePosixPath`, and `re.sub` on unbounded client input, making it vulnerable to ReDoS or CPU/memory exhaustion (DoS) when fed extremely long strings.
**Learning:** Even fast standard library functions like `PurePosixPath` and string replacements can cause significant lag when chained on strings in the megabytes. String processing operations should always bound their inputs first if the input is untrusted and can be arbitrarily large.
**Prevention:** Cap the length of client-provided filename strings early by slicing them (e.g. `filename = filename[-512:]`) before doing more complex string parsing or regex replacements, especially when only the basename suffix is relevant.

## 2025-03-09 - Prevent Resource Exhaustion (DoS) in Authentication Headers
**Vulnerability:** The API accepted unbounded `Authorization` header strings, which were passed directly to `hmac.compare_digest`. Since `hmac.compare_digest` execution time or intermediate string allocations can scale with extremely large inputs, this posed a memory/CPU exhaustion DoS risk.
**Learning:** Even fast constant-time comparison functions can become a bottleneck or cause memory exhaustion if the length of the untrusted input string is not bounded beforehand.
**Prevention:** Explicitly limit the length (e.g., 512 characters) of incoming authorization tokens or headers before performing cryptographic comparisons or allocating memory for them.

## 2025-03-09 - Prevent Resource Exhaustion (DoS) in Authentication Headers
**Vulnerability:** The /parse API accepted unbounded Authorization header strings, which were passed directly to hmac.compare_digest. Since hmac.compare_digest execution time or intermediate string allocations can scale with extremely large inputs, this posed a memory/CPU exhaustion DoS risk.
**Learning:** Even fast constant-time comparison functions can become a bottleneck or cause memory exhaustion if the length of the untrusted input string is not bounded beforehand.
**Prevention:** Explicitly limit the length (e.g., 512 characters) of incoming authorization tokens or headers before performing cryptographic comparisons or allocating memory for them.
10 changes: 10 additions & 0 deletions src/newsdom_api/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,16 @@ def require_authorization(
return
expected = f"Bearer {token}"
provided = authorization or ""

# ๐Ÿ›ก๏ธ Sentinel: Bound length of incoming authentication headers
# to prevent resource exhaustion/DoS via hmac.compare_digest
if len(provided) > 512:
raise HTTPException(
status_code=401,
detail=UNAUTHORIZED_DETAIL,
headers={"WWW-Authenticate": "Bearer"},
)

if not hmac.compare_digest(provided, expected):
raise HTTPException(
status_code=401,
Expand Down
12 changes: 12 additions & 0 deletions tests/test_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,18 @@ def test_parse_accepts_valid_bearer_when_secret_set(monkeypatch, stub_parser):
assert response.status_code == 200


def test_parse_rejects_overly_long_bearer_token(monkeypatch, stub_parser):
monkeypatch.setenv(API_TOKEN_ENV_VAR, "s3cret-token")
client = TestClient(app)
response = client.post(
"/parse",
files=_PDF_FILES,
headers={"Authorization": f"Bearer {'a' * 600}"},
)
assert response.status_code == 401
assert response.json()["detail"] == "Unauthorized"


def test_health_is_unauthenticated_even_when_secret_set(monkeypatch):
monkeypatch.setenv(API_TOKEN_ENV_VAR, "s3cret-token")
client = TestClient(app)
Expand Down
Loading