Skip to content

fix(ci): bump pillow and pytest to resolve audit CVEs#222

Closed
BANANASJIM wants to merge 3 commits into
masterfrom
fix/ci-dep-audit
Closed

fix(ci): bump pillow and pytest to resolve audit CVEs#222
BANANASJIM wants to merge 3 commits into
masterfrom
fix/ci-dep-audit

Conversation

@BANANASJIM
Copy link
Copy Markdown
Owner

@BANANASJIM BANANASJIM commented Apr 16, 2026

Summary

These two CVEs cause the pip-audit CI step to fail on all open PRs (#219, #220, #221).

Test plan

  • pip-audit passes locally with 0 vulnerabilities
  • All 2780 unit tests pass
  • E2E smoke tests pass (pre-push hook)

Summary by CodeRabbit

Release Notes

  • New Features

    • Automatic GPU detection and configuration for replays to use the correct graphics device based on capture metadata.
  • Bug Fixes

    • Improved ARM Performance Studio installation detection and validation.
    • Enhanced environment variable path normalization.
  • Tests

    • Expanded test coverage for GPU detection and ARM Performance Studio environment support.

Two fixes for multi-GPU replay crashes:

1. _is_arm_studio_dir() falsely detected upstream renderdoc builds as
   ARM Performance Studio, triggering RTLD_GLOBAL preload of
   librenderdoc.so which corrupted Vulkan replay. Now requires
   "arm-performance-studio" in the path.

2. ReplayOptions now pins forceGPUVendor/forceGPUDeviceID to the GPU
   that was used for capture, by matching the deviceName from structured
   data against GetAvailableGPUs(). Prevents replay from picking a
   different GPU than what captured the frame.

Also resolves RENDERDOC_PYTHON_PATH relative path handling.
Resolves CVE-2026-40192 (pillow) and CVE-2025-71176 (pytest) that
cause the pip-audit CI step to fail on all open PRs.
Copy link
Copy Markdown

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

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

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 16, 2026

📝 Walkthrough

Walkthrough

The pull request enhances the replay system with GPU detection and matching logic. The daemon now infers the GPU used during capture by reading structured device data, derives appropriate GPU force-settings via ReplayOptions, and applies them when opening captures. ARM Performance Studio detection was refined, and related mocks were updated.

Changes

Cohort / File(s) Summary
Replay GPU Detection
src/rdc/daemon_server.py, tests/mocks/mock_renderdoc.py
Added _match_capture_gpu() to infer capture GPU by matching structured device names against available GPUs with fallback logic. Updated _load_replay and _load_remote_replay to compute ReplayOptions, detect GPU, and set forceGPUVendor / forceGPUDeviceID before opening captures.
Discovery & Path Resolution
src/rdc/discover.py, tests/unit/test_discover.py
Refined ARM Performance Studio detection to require both library files and path component matching. Normalized RENDERDOC_PYTHON_PATH via os.path.abspath() in candidate path building. Added/updated tests for ARM PS directory layout detection.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant DaemonServer
    participant Capture
    participant StructuredData as Structured Data
    participant GPUMatcher
    participant ReplayOptions
    participant RenderDoc

    Client->>DaemonServer: Load replay (capture file)
    DaemonServer->>Capture: Read capture
    Capture-->>DaemonServer: Capture object
    DaemonServer->>Capture: GetStructuredData()
    Capture-->>StructuredData: Device info (physProps.deviceName)
    DaemonServer->>Capture: GetAvailableGPUs()
    Capture-->>DaemonServer: GPU list
    DaemonServer->>GPUMatcher: _match_capture_gpu(capture, structured_data)
    GPUMatcher->>GPUMatcher: Match device name against<br/>available GPUs
    GPUMatcher-->>DaemonServer: GPU vendor & device ID
    DaemonServer->>ReplayOptions: Create with forceGPUVendor,<br/>forceGPUDeviceID
    ReplayOptions-->>DaemonServer: Configured options
    DaemonServer->>RenderDoc: OpenCapture(capture, options)
    RenderDoc-->>Client: Replay ready
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 A capture's GPU we now divine,
Reading structured data, matching fine,
With fallback grace when paths diverge,
Replay options surge and merge,
Perfect devices, each in line! 🎮

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The PR title 'fix(ci): bump pillow and pytest to resolve audit CVEs' refers to dependency bumps for security fixes, but the actual changeset includes substantial GPU matching logic for multi-GPU replay scenarios, ARM Studio detection fixes, and test updates—only a small portion relates to CI dependency bumps. Update the PR title to reflect the primary changes: consider 'fix: GPU matching for multi-GPU systems and ARM Studio detection' or split into separate focused PRs for CI dependencies vs. replay logic fixes.
Docstring Coverage ⚠️ Warning Docstring coverage is 63.64% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/ci-dep-audit
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch fix/ci-dep-audit

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/rdc/daemon_server.py (1)

179-192: Consider adding a comment noting Vulkan-specific parsing.

The structured data traversal specifically looks for vkEnumeratePhysicalDevices, which only applies to Vulkan captures. For D3D/OpenGL captures, the function falls back to the first available GPU. This is reasonable behavior but a brief comment would clarify the intent.

📝 Suggested documentation
+        # Parse Vulkan-specific structured data to find capture GPU name.
+        # For non-Vulkan captures, falls back to first available GPU.
         for i in range(len(sd.chunks)):
             c = sd.chunks[i]
             if c.name == "vkEnumeratePhysicalDevices":
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/rdc/daemon_server.py` around lines 179 - 192, The traversal in the loop
examines sd.chunks and looks explicitly for the Vulkan-specific call name
"vkEnumeratePhysicalDevices" and then checks children named "physProps" and
"deviceName" to match against gpus; add a concise inline comment near this block
(referencing sd.chunks, vkEnumeratePhysicalDevices, physProps, deviceName, and
gpus) stating that this parsing is Vulkan-specific and that for D3D/OpenGL
captures the logic intentionally falls back to the first available GPU, to
clarify intent for future readers.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/rdc/daemon_server.py`:
- Around line 169-196: In _match_capture_gpu, avoid the UnboundLocalError by
initializing gpus before the try block (e.g., gpus = [] or gpus = None) so
references in the except/final return are safe; update the function
(_match_capture_gpu) to set a default for gpus, keep the existing try/except
logic, and ensure the final return uses a safe check (return gpus[0] if gpus
else None) without referencing an uninitialized variable.
- Around line 359-370: The code currently uses _match_capture_gpu(tmp_cap) on
the local capture and writes its vendor/device into
remote_opts.forceGPUVendor/forceGPUDeviceID before calling remote.OpenCapture,
which can pick a GPU that doesn't exist on the remote; instead, remove the
assignment of remote_opts.forceGPUVendor and remote_opts.forceGPUDeviceID when
opening a remote server (i.e., when using remote.OpenCapture) so the remote can
pick NoPreference, or if you must force, query the remote's available GPUs first
and validate the local IDs against that list and log a clear error before
calling remote.OpenCapture; update the block that handles
state.local_capture_path and tmp_cap (and references to
remote_opts.forceGPUVendor/forceGPUDeviceID and _match_capture_gpu) to omit
forcing for remote replay or add remote-side validation+error logging
accordingly.

---

Nitpick comments:
In `@src/rdc/daemon_server.py`:
- Around line 179-192: The traversal in the loop examines sd.chunks and looks
explicitly for the Vulkan-specific call name "vkEnumeratePhysicalDevices" and
then checks children named "physProps" and "deviceName" to match against gpus;
add a concise inline comment near this block (referencing sd.chunks,
vkEnumeratePhysicalDevices, physProps, deviceName, and gpus) stating that this
parsing is Vulkan-specific and that for D3D/OpenGL captures the logic
intentionally falls back to the first available GPU, to clarify intent for
future readers.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a5b14e83-ca4a-4345-ae36-14b33a5e5835

📥 Commits

Reviewing files that changed from the base of the PR and between 97cb316 and a8c59a8.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (4)
  • src/rdc/daemon_server.py
  • src/rdc/discover.py
  • tests/mocks/mock_renderdoc.py
  • tests/unit/test_discover.py

Comment thread src/rdc/daemon_server.py
Comment on lines +169 to +196
def _match_capture_gpu(cap: Any, sd: Any = None) -> Any | None:
"""Find the GPU used for capture by matching structured data against available GPUs."""
try:
gpus = cap.GetAvailableGPUs()
if not gpus:
return None
if len(gpus) == 1:
return gpus[0]
if sd is None:
return gpus[0]
for i in range(len(sd.chunks)):
c = sd.chunks[i]
if c.name == "vkEnumeratePhysicalDevices":
for j in range(c.NumChildren()):
child = c.GetChild(j)
if child.name == "physProps":
for k in range(child.NumChildren()):
prop = child.GetChild(k)
if prop.name == "deviceName":
name = prop.AsString()
for g in gpus:
if g.name == name:
return g
break
except Exception: # noqa: BLE001
pass
return gpus[0] if gpus else None

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Potential UnboundLocalError if GetAvailableGPUs() raises.

If cap.GetAvailableGPUs() at line 172 raises an exception, the except block at line 193 catches it and falls through to line 195, where gpus is referenced but was never assigned.

🐛 Proposed fix
 def _match_capture_gpu(cap: Any, sd: Any = None) -> Any | None:
     """Find the GPU used for capture by matching structured data against available GPUs."""
+    gpus: list[Any] = []
     try:
         gpus = cap.GetAvailableGPUs()
         if not gpus:
             return None
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/rdc/daemon_server.py` around lines 169 - 196, In _match_capture_gpu,
avoid the UnboundLocalError by initializing gpus before the try block (e.g.,
gpus = [] or gpus = None) so references in the except/final return are safe;
update the function (_match_capture_gpu) to set a default for gpus, keep the
existing try/except logic, and ensure the final return uses a safe check (return
gpus[0] if gpus else None) without referencing an uninitialized variable.

Comment thread src/rdc/daemon_server.py
Comment on lines +359 to +370
remote_opts = rd.ReplayOptions()
if state.local_capture_path:
tmp_cap = rd.OpenCaptureFile()
if tmp_cap.OpenFile(state.local_capture_path, "", None) == rd.ResultCode.Succeeded:
gpu = _match_capture_gpu(tmp_cap)
if gpu is not None:
remote_opts.forceGPUVendor = gpu.vendor
remote_opts.forceGPUDeviceID = gpu.deviceID
tmp_cap.Shutdown()

result, controller = remote.OpenCapture(
rd.RemoteServer.NoPreference, remote_path, rd.ReplayOptions(), None
rd.RemoteServer.NoPreference, remote_path, remote_opts, None
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if RemoteServer has a method to get available GPUs
ast-grep --pattern $'class RemoteServer {
  $$$
  GetAvailableGPUs($$$) {
    $$$
  }
  $$$
}'

# Search for any remote GPU enumeration patterns
rg -n -C3 'GetAvailableGPUs|AvailableGPUs' --type=py

Repository: BANANASJIM/rdc-cli

Length of output: 2721


🏁 Script executed:

# Search for RemoteServer class definition
rg -n 'class RemoteServer' --type=py

# Look at the remote module/import to understand what it exposes
rg -n 'import.*[Rr]emote|from.*[Rr]emote' src/rdc/daemon_server.py

# Check the rd module type hints and available GPU-related APIs
fd -e pyi -e py | xargs rg -l 'RemoteServer|remote' | head -10

Repository: BANANASJIM/rdc-cli

Length of output: 435


🏁 Script executed:

# Look at the context around line 359-370 in daemon_server.py
sed -n '350,380p' src/rdc/daemon_server.py

# Check how remote is created/instantiated
rg -n -B5 'remote\.OpenCapture|RemoteServer' src/rdc/daemon_server.py | head -40

# Look for any comments or documentation about GPU matching on remote
rg -n -C5 '_match_capture_gpu' src/rdc/daemon_server.py

Repository: BANANASJIM/rdc-cli

Length of output: 3475


🏁 Script executed:

# Look at how local replay handles GPU matching for comparison
sed -n '210,230p' src/rdc/daemon_server.py

# Search for any remote GPU enumeration patterns or comments
rg -n 'remote.*GPU|GPU.*remote|forceGPU' src/rdc/

# Check if there's any documentation or workarounds for remote GPU issues
rg -n 'NoPreference|forceGPU' src/rdc/ -C2

# Look for test cases about remote GPU matching
fd -e py tests/ | xargs rg -l 'remote.*GPU|GPU.*remote' 2>/dev/null

Repository: BANANASJIM/rdc-cli

Length of output: 2975


🏁 Script executed:

# Look for comments or documentation about remote GPU matching
rg -n 'remote.*GPU|GPU|NoPreference' src/rdc/daemon_server.py -C3

# Check the _match_capture_gpu function in detail
sed -n '169,195p' src/rdc/daemon_server.py

# Look for any error handling or fallback when GPU forcing fails
rg -n 'forceGPU' src/rdc/ -A3 -B1

# Check if there are any issues or TODOs related to remote GPU
rg -n 'TODO|FIXME|BUG|XXX' src/rdc/daemon_server.py | grep -i gpu

Repository: BANANASJIM/rdc-cli

Length of output: 3752


🏁 Script executed:

# Look for the full remote replay function context
sed -n '320,390p' src/rdc/daemon_server.py

# Check if there's any error handling or logging related to remote GPU forcing
rg -n 'remote_opts|RemoteServer' src/rdc/daemon_server.py -C2

# Look for any documentation files that might explain remote GPU handling
fd -e md -e txt | xargs rg -l 'remote.*GPU|GPU' 2>/dev/null | head -5

# Check if structured data is available during remote replay
rg -n 'GetStructuredData' src/rdc/daemon_server.py

Repository: BANANASJIM/rdc-cli

Length of output: 4076


🏁 Script executed:

# Check if there are any tests for remote GPU matching
fd . tests/ -name '*.py' | xargs rg -l 'remote.*GPU|test.*remote.*replay' 2>/dev/null

# Look at the test file for remote replay
find tests -name '*remote*' -type f | head -5

# Check if there's any documentation about the limitation
rg -n 'InitialiseReplay|remote.*GPU' docs -r . 2>/dev/null | head -20

# Check the exact error checking around remote.OpenCapture
sed -n '369,385p' src/rdc/daemon_server.py

Repository: BANANASJIM/rdc-cli

Length of output: 1328


🏁 Script executed:

# Look at the remote replay test to understand expected behavior
cat tests/unit/test_open_remote.py | head -100

# Check if there's any documentation about remote GPU limitations
cat docs-astro/src/pages/docs/remote.astro 2>/dev/null | head -150

# Check the proposal/changelog for any discussion about GPU handling
cat openspec/changes/2026-03-03-bugfix-p2-batch/proposal.md 2>/dev/null | head -200

Repository: BANANASJIM/rdc-cli

Length of output: 13644


Remote GPU forcing uses local capture's available GPUs, not remote server's.

The code calls _match_capture_gpu(tmp_cap) on the local copy of the capture, returning the first available GPU from the local machine. This GPU vendor/device ID is then passed to remote.OpenCapture() via remote_opts.forceGPUVendor and remote_opts.forceGPUDeviceID.

If the remote server has different GPUs available than the local machine, the forced GPU may not exist on the remote, causing undefined behavior. The RenderDoc API's rd.RemoteServer.NoPreference fallback suggests graceful degradation is possible, but there is no explicit handling or logging if GPU forcing fails.

Consider either:

  1. Omitting GPU forcing for remote replay (let NoPreference handle GPU selection on the remote)
  2. Adding explicit error handling or logging if the forced GPU is not available
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/rdc/daemon_server.py` around lines 359 - 370, The code currently uses
_match_capture_gpu(tmp_cap) on the local capture and writes its vendor/device
into remote_opts.forceGPUVendor/forceGPUDeviceID before calling
remote.OpenCapture, which can pick a GPU that doesn't exist on the remote;
instead, remove the assignment of remote_opts.forceGPUVendor and
remote_opts.forceGPUDeviceID when opening a remote server (i.e., when using
remote.OpenCapture) so the remote can pick NoPreference, or if you must force,
query the remote's available GPUs first and validate the local IDs against that
list and log a clear error before calling remote.OpenCapture; update the block
that handles state.local_capture_path and tmp_cap (and references to
remote_opts.forceGPUVendor/forceGPUDeviceID and _match_capture_gpu) to omit
forcing for remote replay or add remote-side validation+error logging
accordingly.

Copy link
Copy Markdown

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

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

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@BANANASJIM
Copy link
Copy Markdown
Owner Author

Superseded — the uv.lock bump was cherry-picked into #219/#220/#221 and merged with #219.

@BANANASJIM BANANASJIM closed this Apr 16, 2026
@BANANASJIM BANANASJIM deleted the fix/ci-dep-audit branch May 19, 2026 00:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant