Skip to content
Open
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
3 changes: 3 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@ autotest>=0.16.2; python_version < '3.0'
aexpect>=1.6.4;python_version >= '3.0'
aexpect>1.5.0; python_version < '3.0'
zipp<=1.2.0; python_version < '3.0'
google-generativeai
Pillow
importlib-metadata>=4.6; python_version < '3.10'
Comment on lines +5 to +7
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check latest versions of the dependencies
echo "=== Checking google-generativeai latest version ==="
pip index versions google-generativeai 2>/dev/null | head -20 || curl -s https://pypi.org/pypi/google-generativeai/json | python3 -c "import sys, json; data = json.load(sys.stdin); print('Latest version:', data['info']['version']); print('All recent versions:'); versions = sorted(data['releases'].keys(), reverse=True)[:10]; print('\n'.join(versions))"

echo ""
echo "=== Checking Pillow latest version ==="
curl -s https://pypi.org/pypi/Pillow/json | python3 -c "import sys, json; data = json.load(sys.stdin); print('Latest version:', data['info']['version']); print('All recent versions:'); versions = sorted(data['releases'].keys(), reverse=True)[:10]; print('\n'.join(versions))"

Repository: avocado-framework/avocado-vt

Length of output: 200


🌐 Web query:

latest stable version google-generativeai PyPI 2025

💡 Result:

The latest stable PyPI release of google-generativeai is version 0.8.6 — uploaded Dec 16, 2025. [1][2]

Note: the package is marked legacy/deprecated with support ended Nov 30, 2025. [1]

Sources:
[1] PyPI project page for google-generativeai.
[2] Safety DB package entry for google-generativeai.


🌐 Web query:

latest stable version Pillow PyPI 2025

💡 Result:

Latest stable Pillow on PyPI (as of Dec 30, 2025): 12.0.0 — released/uploaded Oct 15, 2025. [1]

Sources
[1] PyPI project page for Pillow (release files and upload date).


🌐 Web query:

google-generativeai deprecated EOL security November 2025

💡 Result:

Short summary — relevant deprecations / EOL dates in Nov 2025:

  • Google Cloud: the Generative AI module in the Vertex AI SDK was deprecated on June 24, 2025 and is scheduled for removal on June 24, 2026 (so not EOL in Nov 2025). [1]
  • Imagen 4 preview models are retiring on November 30, 2025 (migrate to the Imagen 4 GA models). [1][2]
  • Multiple Gemini / Gemini‑related preview models were announced for shutdown in November–December 2025 (example: several Gemini and Veo preview models listed with Nov/Dec shutdown dates). [3]
  • Google Security (Chronicle) also lists some feature/log connector removals on/around November 30, 2025 (e.g., NETSKOPE_ALERT v1 removal). If you mean security tooling EOLs, check Chronicle deprecations for exact items/dates. [4]

If you want, I can fetch the full deprecation pages and list every affected model/feature and exact shutdown dates for your account/region.


🌐 Web query:

Pillow CVE security vulnerabilities 2025

💡 Result:

Summary — Pillow CVE(s) in 2025

  • CVE-2025-48379: heap-based buffer overflow when writing large (>64 KB encoded) DDS/BCn images. Affects Pillow >= 11.2.0 and < 11.3.0; patched in Pillow 11.3.0 (published July 1, 2025). Avoid saving untrusted DDS images and upgrade to 11.3.0+ immediately. [1][2][3]

References

  • NVD entry for CVE-2025-48379. [1]
  • Rapid7 / security advisories summarizing the issue and fix (Pillow 11.3.0). [2]
  • Wiz vulnerability page / additional technical details and mitigation. [3]

🌐 Web query:

google-generativeai CVE security vulnerabilities 2025

💡 Result:

Here are notable CVEs and security bulletins (2025) related to Google’s generative-AI products (Vertex AI / Gemini) and adjacent Google AI incidents:

  • CVE-2025-11915 — Connection desynchronization (HTTP request/response smuggling) in Vertex AI model proxying; fixed/rolled out by Google (GCP-2025-059). Published/linked in Google Cloud security bulletins (published 2025-10-21; CVE entry in NVD). [1][2]

  • GCP-2025-059 (Vertex AI security bulletin) — streaming requests misrouting between recipients for certain third‑party and self‑deployed models; fixes rolled out Sept 26–28, 2025. (Google’s bulletin page lists the incident and CVE mapping). [1]

  • CVE-2025-6965 / related Google incident — media coverage reporting a patched memory‑corruption vuln (patched June 2025) where Google said an AI agent helped thwart exploitation; reported coverage (SecurityWeek). (Note: follow-up technical details limited in public reporting). [3]

  • Related research / disclosure discussions — public researcher posts and press coverage in 2025 raised issues with model input handling (prompt/ASCII smuggling and prompt‑injection risks affecting Gemini and integrated agents); Google’s position varied by report. See coverage/summaries. [4]

If you want, I can:

  • pull the full NVD/CVE entries and vendor advisories for each CVE (with direct references), or
  • compile a chronological list of all Google Cloud/Vertex AI CVEs in 2025 with NVD links.

Sources: Google Cloud Vertex AI security bulletins (GCP-2025-059) [1]; NVD CVE entry for CVE‑2025‑11915 (NIST) [2]; SecurityWeek reporting on CVE‑2025‑6965 and Google AI agent use [3]; reporting on Gemini/input‑handling research [4].


Pin dependency versions and address end-of-life package.

The new dependencies lack version specifications. Additionally, google-generativeai is marked as deprecated/legacy with support ended November 30, 2025, and should not be used for new development. For Pillow, the suggested version range is outdated; version 12.0.0 is now stable, and versions 11.3.0+ include a critical security fix for CVE-2025-48379 (heap-based buffer overflow in DDS/BCn image handling).

Either replace google-generativeai with an alternative maintained package, or if it must be retained, document the EOL status and migration plan. For Pillow, pin to the latest stable release:

-google-generativeai
-Pillow
+Pillow>=12.0.0,<13.0.0

(Remove google-generativeai or provide justification for using an EOL package.)

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
google-generativeai
Pillow
importlib-metadata>=4.6; python_version < '3.10'
Pillow>=12.0.0,<13.0.0
importlib-metadata>=4.6; python_version < '3.10'
🤖 Prompt for AI Agents
In requirements.txt lines 5-7: the dependencies are unpinned and
`google-generativeai` is EOL/legacy; replace or remove it (or keep only with an
inline comment documenting the EOL status and a concrete migration plan to a
maintained alternative), and pin versions for the other packages to safe stable
releases (e.g. set Pillow to the latest stable release that includes the
CVE-2025-48379 fix, and pin importlib-metadata to a specific supported range) so
installs are reproducible and secure; update the file with those version
constraints and add a short comment explaining any retained EOL package and its
replacement plan.

169 changes: 169 additions & 0 deletions virttest/ppm_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,180 @@
except ImportError:
import md5

try:
# Monkey patch importlib.metadata.packages_distributions for Python < 3.10
# This is needed because some google libraries expect it to exist in stdlib importlib.metadata
# but it was only added in Python 3.10.
import sys
if sys.version_info < (3, 10):
import importlib.metadata
import importlib_metadata
if not hasattr(importlib.metadata, "packages_distributions"):
importlib.metadata.packages_distributions = importlib_metadata.packages_distributions
except ImportError:
pass

try:
import google.generativeai as genai
except ImportError:
genai = None
logging.getLogger("avocado.app").warning(
"google-generativeai library not found. Visual verification with Gemini is disabled."
)

# Some directory/filename utils, for consistency

LOG = logging.getLogger("avocado." + __name__)


def verify_screen_with_gemini(
image_path,
prompt,
api_key=None,
model_name="gemini-pro-vision", # Use gemini-pro-vision for images
save_failed_image=True,
results_dir=None,
resize_max_dim=1024,
):
"""
Verify screen content using Google Gemini API.
:param image_path: Path to the image file (PPM format expected from QEMU).
:param prompt: Question to ask about the image.
:param api_key: Gemini API Key. If None, uses GEMINI_API_KEY env var.
:param model_name: Model version to use.
:param save_failed_image: Whether to save the image if validation "fails" (logic depends on prompt).
:param results_dir: Directory to save failed images.
:param resize_max_dim: Max dimension to resize image to (maintains aspect ratio).
Set to None to disable resizing.
:return: The text response from Gemini (stripped).
"""
if not genai:
raise ImportError(
"google-generativeai library is required for this feature. "
"Please install it using 'pip install google-generativeai'."
)

if not api_key:
api_key = os.environ.get("GEMINI_API_KEY")

if not api_key:
raise ValueError("Gemini API Key is required (set GEMINI_API_KEY env var).")

# Configure Proxy if set in environment
# requests/urllib3 usually pick up HTTPS_PROXY automatically, but we ensure it's available.
if "HTTPS_PROXY" not in os.environ and "https_proxy" not in os.environ:
LOG.warning("No HTTPS_PROXY set. Gemini API access might fail if you are behind a firewall.")

# Force REST transport to avoid gRPC proxy issues and ensure better compatibility
genai.configure(api_key=api_key, transport="rest")

if not Image:
raise ImportError("Pillow (PIL) is required to process images.")

try:
# Open and process image
with Image.open(image_path) as img:
# Resize if requested to save bandwidth/quota
if resize_max_dim:
img.thumbnail((resize_max_dim, resize_max_dim))

# Convert to RGB (PPM is RGB, but good safety measure) and save to JPEG in memory
# JPEG is much smaller than PPM or PNG
import io

img_byte_arr = io.BytesIO()
img.convert("RGB").save(img_byte_arr, format="JPEG", quality=85)
img_jpeg = Image.open(img_byte_arr)

# Candidate models to try, in order of preference
candidate_models = [
"gemini-flash-latest", # Available per logs
"gemini-2.0-flash", # Available per logs
"gemini-2.5-flash", # Available per logs
"gemini-pro-latest", # Available per logs
model_name, # The one passed in argument
"gemini-1.5-flash",
"gemini-1.5-pro",
"gemini-pro-vision",
]
# Remove duplicates while preserving order
candidate_models = list(dict.fromkeys(candidate_models))

response = None
last_error = None

for model_candidate in candidate_models:
try:
LOG.info("Trying Gemini model: %s", model_candidate)
model = genai.GenerativeModel(model_candidate)

# Retry logic for each model
max_retries = 2
for attempt in range(max_retries):
try:
response = model.generate_content(
[prompt, img_jpeg],
generation_config=genai.types.GenerationConfig(
temperature=0.1
)
)
break # Success inner loop
except Exception as e:
if "404" in str(e) or "not found" in str(e).lower():
# Model not found, break inner retry to try next model
raise e
if attempt == max_retries - 1:
raise e
LOG.warning("Gemini API call failed (attempt %d/%d) for model %s: %s. Retrying...", attempt + 1, max_retries, model_candidate, e)
time.sleep(2)

if response:
break # Success outer loop

except Exception as e:
last_error = e
LOG.warning("Model %s failed: %s", model_candidate, e)
continue

if not response:
LOG.error("All candidate models failed. Listing available models...")
try:
for m in genai.list_models():
LOG.info("Available model: %s (methods: %s)", m.name, m.supported_generation_methods)
except Exception as list_e:
LOG.error("Failed to list models: %s", list_e)

raise last_error or Exception("No working Gemini model found")

result_text = response.text.strip()

# Simple heuristic: if prompt asks for YES/NO and we get NO, save image
if save_failed_image and results_dir:
# This logic is loose; caller should decide pass/fail, but we help debug here.
# If the response starts with "No" (case insensitive), we treat it as suspicious.
if result_text.lower().startswith("no"):
try:
timestamp = time.strftime("%Y%m%d_%H%M%S")
fail_filename = "gemini_fail_%s.jpg" % timestamp
fail_path = os.path.join(results_dir, fail_filename)
if not os.path.exists(results_dir):
os.makedirs(results_dir)
# Save the compressed/resized version we actually sent
with open(fail_path, "wb") as f:
f.write(img_byte_arr.getvalue())
LOG.info("Saved failed visual check image to: %s", fail_path)
except Exception as e:
LOG.error("Failed to save debug image: %s", e)
Comment on lines +188 to +203
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Reconsider the failure detection logic.

The logic that treats responses starting with "no" as failures is too simplistic and prompt-dependent. For example:

  • Prompt: "Is this screen corrupted?" → "No" means success
  • Prompt: "Does this screen show the login page?" → "No" means failure

This heuristic will produce incorrect results depending on how the prompt is phrased.

Consider these alternatives:

  1. Remove the automatic saving logic and let the caller decide based on the response:

    result_text = response.text.strip()
    return result_text
    # Caller can then save image if needed based on their logic
  2. Add an explicit parameter for expected response or failure keywords:

    def verify_screen_with_gemini(
        ...
        failure_keywords=None,  # e.g., ["no", "incorrect", "failed"]
        ...
    ):
  3. Document the assumption clearly in the docstring that this function expects yes/no prompts where "no" indicates failure, and add a parameter to disable this behavior.

The current implementation may surprise users and lead to incorrect behavior.

🧰 Tools
🪛 Ruff (0.14.10)

202-202: Do not catch blind exception: Exception

(BLE001)


203-203: Use logging.exception instead of logging.error

Replace with exception

(TRY400)

🤖 Prompt for AI Agents
In virttest/ppm_utils.py around lines 188-203, the current heuristic that treats
any response starting with "no" as a failure is too brittle and
prompt-dependent; update the function to remove this hardcoded behavior and
instead (a) add a configurable parameter (e.g., failure_keywords or an explicit
boolean like auto_save_on_no with default False) so callers control what
constitutes failure, (b) only save the debug image when the caller-supplied
failure condition is met, and (c) update the docstring to document the default
behavior and the new parameter; ensure any file writes remain wrapped in the
existing try/except and keep logging of success/failure paths.


return result_text

except Exception as e:
LOG.error("Gemini API call failed: %s", e)
# We re-raise to let the test fail with ERROR status
raise


def _md5eval(data):
"""
Returns a md5 hash evaluator. This function is implemented in order to
Expand Down
Loading