mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-31 06:56:59 +00:00
* test: add a manually dispatched integration trial on larger runners Runs the providers, database and management integration groups on 16-core GitHub-hosted runners with Postgres and Redis as service containers, to measure wall time and how far each group scales with more workers run_integration.sh now keeps a preset INTEGRATION_WORKERS, takes the service container names from the environment, and seeds test order from the GitHub run when there is no CircleCI one * test: run the integration trial on push to this branch * test: give integration trial workers a local redis-server * test: run the providers trial on a 64-core runner with 32 workers The 16-core run reached 53% of providers in 45 minutes with 16 workers, which points at CPU. Postgres gets max_connections=1000 and a 1 GB shm because one server now backs 32 worker proxies, where each CircleCI machine backs 4 * test: run every integration group in the trial on sized runners * test: add a scan that refuses to upload integration results holding the license * test: drop the member-name pass the archive byte scan already covers * test: use a secret whose base64 and base64url encodings differ * ci: run integration groups on run-ci pull requests and scheduled main runs * ci: scope the integration license to a protected environment
119 lines
4.3 KiB
Python
119 lines
4.3 KiB
Python
import base64
|
|
import io
|
|
import os
|
|
import sys
|
|
import urllib.parse
|
|
import zipfile
|
|
from collections.abc import Callable, Iterator, Mapping
|
|
from dataclasses import dataclass
|
|
from functools import reduce
|
|
from itertools import chain
|
|
from pathlib import Path
|
|
from types import MappingProxyType
|
|
from typing import Final, TextIO
|
|
|
|
SECRET_VARIABLE: Final = "SCANNED_SECRET"
|
|
MIN_ENCODED_LENGTH: Final = 12
|
|
MAX_ARCHIVE_DEPTH: Final = 3
|
|
MASK: Final = "***"
|
|
|
|
Forms = tuple[tuple[str, str], ...]
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class Hit:
|
|
location: str
|
|
form: str
|
|
|
|
|
|
def _aligned_encodings(raw: bytes, name: str, encode: Callable[[bytes], bytes]) -> Iterator[tuple[str, str]]:
|
|
for offset in range(3):
|
|
chunk: Final = raw[offset:]
|
|
yield f"{name} offset {offset}", encode(chunk[: len(chunk) - len(chunk) % 3]).decode()
|
|
|
|
|
|
def secret_forms(secret: str) -> Forms:
|
|
encoded: Final = (
|
|
("url-encoded", urllib.parse.quote(secret, safe="")),
|
|
*_aligned_encodings(secret.encode(), "base64", base64.b64encode),
|
|
*_aligned_encodings(secret.encode(), "base64url", base64.urlsafe_b64encode),
|
|
)
|
|
by_value: Final = MappingProxyType(
|
|
{value: name for name, value in encoded if value != secret and len(value) >= MIN_ENCODED_LENGTH}
|
|
)
|
|
return (("raw", secret), *((name, value) for value, name in by_value.items()))
|
|
|
|
|
|
def mask(text: str, forms: Forms) -> str:
|
|
return reduce(lambda masked, form: masked.replace(form[1], MASK), forms, text)
|
|
|
|
|
|
def _name_hits(location: str, name: str, forms: Forms) -> Iterator[Hit]:
|
|
for form, value in forms:
|
|
if value in name:
|
|
yield Hit(mask(location, forms), f"{form} in name")
|
|
|
|
|
|
def _content_hits(location: str, data: bytes, forms: Forms, depth: int) -> Iterator[Hit]:
|
|
for form, value in forms:
|
|
if value.encode() in data:
|
|
yield Hit(mask(location, forms), form)
|
|
if depth >= MAX_ARCHIVE_DEPTH or not zipfile.is_zipfile(io.BytesIO(data)):
|
|
return
|
|
with zipfile.ZipFile(io.BytesIO(data)) as archive:
|
|
for member in archive.infolist():
|
|
if member.is_dir():
|
|
continue
|
|
yield from _content_hits(f"{location}!{member.filename}", archive.read(member), forms, depth + 1)
|
|
|
|
|
|
def _walk(root: Path) -> Iterator[tuple[Path, bool]]:
|
|
for directory, subdirectories, files in os.walk(root):
|
|
yield from ((Path(directory, name), True) for name in subdirectories if Path(directory, name).is_symlink())
|
|
yield from ((Path(directory, name), False) for name in files)
|
|
|
|
|
|
def _path_hits(root: Path, path: Path, linked_directory: bool, forms: Forms) -> Iterator[Hit]:
|
|
location: Final = str(path.relative_to(root))
|
|
if linked_directory:
|
|
yield Hit(mask(location, forms), "directory symlink, not scanned")
|
|
return
|
|
yield from _name_hits(location, location, forms)
|
|
yield from _content_hits(location, path.read_bytes(), forms, 0)
|
|
|
|
|
|
def scan(root: Path, secret: str) -> tuple[int, tuple[Hit, ...]]:
|
|
forms: Final = secret_forms(secret)
|
|
entries: Final = tuple(_walk(root))
|
|
hits: Final = tuple(chain.from_iterable(_path_hits(root, path, linked, forms) for path, linked in entries))
|
|
return len(entries), hits
|
|
|
|
|
|
def main(argv: tuple[str, ...], environ: Mapping[str, str], out: TextIO) -> int:
|
|
if len(argv) != 1:
|
|
print(f"usage: assert_secret_absent.py <directory>, with the secret in ${SECRET_VARIABLE}", file=out)
|
|
return 2
|
|
secret: Final = environ.get(SECRET_VARIABLE, "")
|
|
root: Final = Path(argv[0])
|
|
if not secret:
|
|
print(f"{SECRET_VARIABLE} is empty, nothing to scan for", file=out)
|
|
return 0
|
|
if not root.is_dir():
|
|
print(f"{root} is not a directory, nothing to scan", file=out)
|
|
return 0
|
|
try:
|
|
scanned, hits = scan(root, secret)
|
|
except Exception as error:
|
|
print(f"scan failed with {type(error).__name__}, treating the results as unsafe", file=out)
|
|
return 1
|
|
for hit in hits:
|
|
print(f"secret found: {hit.location} ({hit.form})", file=out)
|
|
if hits:
|
|
print(f"{len(hits)} copies of the secret under {root}, refusing to upload", file=out)
|
|
return 1
|
|
print(f"no copy of the secret in {scanned} files under {root}", file=out)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main(tuple(sys.argv[1:]), os.environ, sys.stdout))
|