openai_server: constant-time API key check (hmac.compare_digest) + resource_plan: csv-parse GPU names (fixes comma-in-name drop) (#186)
P6: discover_gpus() used line.split(',', 3) to parse nvidia-smi CSV output.
If the GPU name contains a comma (e.g. 'Tesla, Inc. V100'), split produces
more than 4 fields, the int() parse fails on the wrong field, and the GPU
is silently dropped — doctor reports 'no NVIDIA device detected' with no
clue why. Fixed by using the csv module (handles quoted fields correctly).
P7: require_auth() used plain string != comparison for the API key
('Authorization' header vs expected 'Bearer <key>'). This enables a
timing side-channel that could leak the key byte-by-byte. Low impact when
bound to localhost (default), but serve() only warns when host is
non-localhost without a key, and users do expose on 0.0.0.0. Fixed by
using hmac.compare_digest (constant-time comparison).
Co-authored-by: woolcoxm <13604288+woolcoxm@users.noreply.github.com>
This commit is contained in:
+7
-3
@@ -672,9 +672,13 @@ class APIHandler(BaseHTTPRequestHandler):
|
|||||||
self.send_header("Vary", "Origin")
|
self.send_header("Vary", "Origin")
|
||||||
|
|
||||||
def require_auth(self):
|
def require_auth(self):
|
||||||
if self.server.api_key and self.headers.get("Authorization") != f"Bearer {self.server.api_key}":
|
if self.server.api_key:
|
||||||
raise APIError(401, "Invalid or missing API key.", None, "invalid_api_key",
|
import hmac
|
||||||
"authentication_error")
|
provided = self.headers.get("Authorization", "")
|
||||||
|
expected = f"Bearer {self.server.api_key}"
|
||||||
|
if not hmac.compare_digest(provided, expected):
|
||||||
|
raise APIError(401, "Invalid or missing API key.", None, "invalid_api_key",
|
||||||
|
"authentication_error")
|
||||||
|
|
||||||
def read_json(self):
|
def read_json(self):
|
||||||
try:
|
try:
|
||||||
|
|||||||
+3
-2
@@ -151,8 +151,9 @@ def discover_gpus():
|
|||||||
except (OSError, subprocess.SubprocessError):
|
except (OSError, subprocess.SubprocessError):
|
||||||
return []
|
return []
|
||||||
devices = []
|
devices = []
|
||||||
for line in result.stdout.splitlines():
|
import csv
|
||||||
fields = [field.strip() for field in line.split(",", 3)]
|
for fields in csv.reader(result.stdout.splitlines()):
|
||||||
|
fields = [f.strip() for f in fields]
|
||||||
if len(fields) != 4:
|
if len(fields) != 4:
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in New Issue
Block a user