Compare commits

...

1 Commits

Author SHA1 Message Date
Cursor Agent
f99bafa429 fix: clear CodeQL weak sensitive hashing on model catalog cache key
SHA-256 of API key material in _cache_key trips
py/weak-sensitive-data-hashing (CodeQL alert #64). Use HMAC-SHA256 with
the credential as the key and a fixed app message so the cache partition
id stays non-reversible without password-style hashing.

Co-authored-by: Rip&Tear <theCyberTech@users.noreply.github.com>
2026-08-05 13:21:29 +00:00
2 changed files with 19 additions and 1 deletions

View File

@@ -25,6 +25,7 @@ from __future__ import annotations
from collections.abc import Callable
import contextlib
import hashlib
import hmac
import json
import os
from pathlib import Path
@@ -626,7 +627,14 @@ def _cache_key(provider_key: str) -> str:
api_key = _provider_api_key(provider_key)
if not api_key:
return f"{provider_key}#nokey"
digest = hashlib.sha256(api_key.encode("utf-8")).hexdigest()[:12]
# HMAC with the credential as the key (not as hash input). SHA-256 alone on
# API-key material trips CodeQL py/weak-sensitive-data-hashing; keyed HMAC is
# the right construction for a local cache partition id.
digest = hmac.new(
api_key.encode("utf-8"),
b"crewai.model_catalog.cache_v1",
hashlib.sha256,
).hexdigest()[:12]
return f"{provider_key}#{digest}"

View File

@@ -2,6 +2,8 @@
from __future__ import annotations
import hashlib
import hmac
import json
import time
@@ -583,6 +585,14 @@ def test_cache_key_hashes_key_and_never_stores_it(monkeypatch):
key = mc._cache_key("openai")
assert key.startswith("openai#") and key != "openai#nokey"
assert "sk-super-secret" not in key # only a digest, never the raw key
# Credential is the HMAC key (not SHA-256 hash input) so CodeQL
# py/weak-sensitive-data-hashing does not flag password-style hashing.
expected = hmac.new(
b"sk-super-secret",
b"crewai.model_catalog.cache_v1",
hashlib.sha256,
).hexdigest()[:12]
assert key == f"openai#{expected}"
def test_dynamic_cache_expires_after_catalog_ttl(monkeypatch):