Back to Blog
critical SEVERITY1 min read

qemu-img Image Fetcher Hardened Against Symlink Swaps

A VM provisioning script that downloads a cloud image to a fixed path and then runs `qemu-img info` on it had no check that the path was still a regular file by the time the inspection call ran. An automated CWE-78 scan flagged the pattern as command-injection-adjacent; the fix adds an explicit `os.path.islink()` guard so the script refuses to process a symlink instead of silently inspecting whatever file the link resolves to.

O
By Orbis AppSec
•Technically reviewed by Anupam Mediratta•Published October 17, 2026•Reviewed October 17, 2026

The Vulnerability Explained

Before the fix, the inspection step looked like this:

```python
src = '/tmp/cloud.img'
if not os.path.isfile(src) or os.path.getsize(src) < 10_000_000:
raise SystemExit(f'cloud image too small or miss

Prevention and further reading

Frequently Asked Questions

Does the call to `qemu-img` in this script use `shell=True` anywhere?

No. It passes arguments as a list to `subprocess.check_output()`, which is why the PR author concluded this isn't demonstrable shell-based command injection.

What does the new `os.path.islink()` check actually stop?

It makes the script abort if the fixed download path has been replaced with a symlink, preventing `qemu-img info` from parsing whatever file that symlink points to instead of the originally downloaded image.

Is this fix tied to a specific package version or CVE?

No — it's a first-party script change with no CVE, GHSA, or package version assigned; it's tracked only by the PR that added the `os.path.islink()` guard.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #471

Related Articles

critical

BrowserLauncher.openURL() Injects Args Into Runtime.exec()

The desktop `BrowserLauncher.openURL(String url)` helper built a Windows command line by concatenating its `fixedURL` value onto `"rundll32.exe url.dll,FileProtocolHandler "` and handing the whole string to `Runtime.getRuntime().exec()`. Because the single-string form of `exec()` tokenizes on whitespace, a URL containing spaces or metacharacters was split into extra process arguments, giving an attacker control over what `rundll32.exe` was asked to open. The fix replaces the concatenation with `

critical

ffmpeg.Probe() Protocol Abuse in the GetSnapshot Thumbnailer

The local storage driver's thumbnail helpers — `GetSnapshot()` and `resizeImageToBufferWithFFmpegGo()` — handed user-controlled paths to `ffmpeg.Input()` and `ffmpeg.Probe()` with no restriction on which FFmpeg protocols those processes could open. A file that passed the existing `sanitizeFilePath` check (absolute, cleaned, regular file) could still be an HLS playlist or concat script pointing at `http://`, `subfile:` or `file:` targets, turning thumbnail generation into SSRF and arbitrary file

critical

webapp-testing Server Script: Shell Injection via Unquoted Command

A critical command injection vulnerability in a web server testing utility allowed attackers to execute arbitrary shell commands by injecting metacharacters into command-line arguments. The fix removes shell interpretation and safely tokenizes user-supplied commands using `shlex.split()`, while preserving support for legitimate directory-change workflows.

high

THiNX Device Platform: Command Injection via source.owner in Git Fetch

The THiNX device management platform fixed a critical vulnerability where the `source.owner` parameter flowed directly into shell commands during git repository operations. An attacker with source configuration access could inject arbitrary shell commands by providing malicious owner values containing metacharacters like `; curl`.

critical

backtest.js CLI args: Unvalidated --days and --start Options

The backtest.js command-line tool accepted --days and --start arguments without validation, creating a command injection vector when the script is invoked by upstream processes with untrusted input. The fix adds strict input validation: --days must be a positive integer ≤ 3650, and --start must match the YYYY-MM-DD date format.

critical

ARTIFICIAL_ANALYSIS_API_KEY: Unvalidated os.getenv in Model Sync

A model-catalogue sync utility read the `ARTIFICIAL_ANALYSIS_API_KEY` environment variable with a bare `os.getenv()` call and passed whatever it found straight into `fetch_intelligence_from_api()`. Any malformed value — a trailing newline from an `--env-file`, a whitespace-only placeholder, or a pasted `Bearer …` prefix — was treated as a valid credential and forwarded into an outbound HTTP request, where it could surface in CI error output. The fix normalizes the value, validates it against a s