fastapi-gsap/.venv/lib/python3.12/site-packages/websockets/utils.py
Tyler J King e744336385 fix: capability enforcement, credential safety, atomic delegations, input validation
C-6: ConnectorRuntime enforces capability_mask per operation.
     READ-only ACs cannot invoke MUTATE operations (wipe, lock, retire).
C-7: AC validated against database (exists, active, not expired)
     before connector invocation.
C-9: Delegated AC capability bounded by delegator's capability.
C-10: Command counter uses atomic SQL increment with limit check.
M-23: expire_stale() uses same atomic SQL pattern.

H-1: Sensitive credential fields hidden from repr/logs via repr=False.
H-2: Stub backend requires ALLOW_STUB_CREDENTIALS=true to activate.
H-3: Kerberos backend raises CredentialResolutionError instead of
     returning stub ticket.
H-4: Chronicle INTENT emitted before execution, RESULT after.
H-5: device_id validated as UUID before Graph API URL interpolation.
H-8: ConnectorRuntime enforces governance for all connector invocations.

Signed-off-by: Tyler King <tking@guildhouse.dev>
2026-04-14 08:13:27 -04:00

53 lines
1.2 KiB
Python

from __future__ import annotations
import base64
import hashlib
import secrets
import sys
from .typing import BytesLike
__all__ = ["accept_key", "apply_mask"]
GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
def generate_key() -> str:
"""
Generate a random key for the Sec-WebSocket-Key header.
"""
key = secrets.token_bytes(16)
return base64.b64encode(key).decode()
def accept_key(key: str) -> str:
"""
Compute the value of the Sec-WebSocket-Accept header.
Args:
key: Value of the Sec-WebSocket-Key header.
"""
sha1 = hashlib.sha1((key + GUID).encode()).digest()
return base64.b64encode(sha1).decode()
def apply_mask(data: BytesLike, mask: bytes | bytearray) -> bytes:
"""
Apply masking to the data of a WebSocket message.
Args:
data: Data to mask.
mask: 4-bytes mask.
"""
if len(mask) != 4:
raise ValueError("mask must contain 4 bytes")
data_int = int.from_bytes(data, sys.byteorder)
mask_repeated = mask * (len(data) // 4) + mask[: len(data) % 4]
mask_int = int.from_bytes(mask_repeated, sys.byteorder)
return (data_int ^ mask_int).to_bytes(len(data), sys.byteorder)