Safeguard
Industry Analysis

Insecure Temporary File Creation

Insecure temp file creation (CWE-377) still causes real CVEs today — from JUnit4 to npm's tmp package. Here's how the race condition works and how to stop it.

Aman Khan
AppSec Engineer
7 min read

In August 2020, security researchers disclosed CVE-2020-15250: JUnit4's widely used TemporaryFolder test rule was creating temporary files and directories with world-readable, world-writable permissions on Unix-like systems. Millions of CI pipelines that relied on JUnit4 to generate scratch files during test runs were quietly leaking test fixtures, credentials, and intermediate build artifacts to any local user on a shared build host. Two years later, CVE-2022-24707 hit the npm ecosystem's tmp package — downloaded tens of millions of times a week — where a symlink could be planted in the temp directory before the package wrote to it, letting an attacker redirect writes to arbitrary paths. Neither bug required a memory corruption exploit or a clever bypass. Both came down to one of the oldest mistakes in software: creating a temporary file in a shared location with a guessable name and no atomicity guarantee. This is insecure temporary file creation, and it remains one of the most persistent, most preventable classes of vulnerability in modern software supply chains.

What Is Insecure Temporary File Creation?

Insecure temporary file creation, tracked as CWE-377, happens when software generates a temp file or directory using a predictable name, a world-writable shared directory, or a check-then-act sequence that leaves a window for another process to interfere. The classic pattern looks deceptively simple: call a function like tmpnam() to get a "unique" filename, then separately open and write to it. Between the naming step and the write step, nothing stops a second process — malicious or not — from creating a file, symlink, or directory at that exact path first. Because most temp file APIs historically wrote into a single shared directory (/tmp on Unix, %TEMP% on Windows) with permissions open to every local user, this race condition was trivial to win on multi-user systems. CWE-377 is a specific case of the broader CWE-367 "time-of-check to time-of-use" (TOCTOU) race condition family, and it has appeared in MITRE's CWE catalog since the list's earliest versions because it predates almost every other class of software vulnerability still being exploited today.

Why Do Temp File Race Conditions Still Happen in 2026?

They still happen because the insecure APIs that cause them were never fully removed from the ecosystems developers use every day, only marked deprecated. Python's tempfile.mktemp() has carried an explicit security warning since Python 2.3 shipped in 2003 — it returns a filename without creating the file, guaranteeing a race window — yet static analysis of open-source repositories still turns up fresh calls to it in scripts and internal tooling written this year. C's tmpnam() and mktemp(3) carry the same warning in their man pages and are still linked into legacy build systems, embedded firmware, and vendored C dependencies that few teams re-audit. The problem compounds in language ecosystems that wrap these primitives: a helper library written once in 2015 to "generate a scratch file" can get pulled in as a transitive dependency by hundreds of downstream packages, silently propagating the same race condition into codebases whose authors never called an insecure API directly. Containerized and cloud-native environments reduce but do not eliminate the risk — shared build caches, CI runners, and multi-tenant Kubernetes nodes still expose shared temp paths across workloads.

What Real-World Vulnerabilities Has This Caused?

It has caused arbitrary file overwrites, privilege escalation, and information disclosure across some of the most widely deployed software of the last three decades. Beyond the JUnit4 and npm tmp cases above, the pattern has a long history: Unix system utilities and daemons throughout the 1990s and 2000s — including versions of sendmail, various /tmp-writing shell scripts, and X11 utilities — were repeatedly patched for symlink attacks where a local attacker pre-created a symlink at a predictable temp path, causing a privileged process to overwrite a system file like /etc/passwd when it "cleaned up" its temp file. More recently, CVE-2022-24707 specifically allowed an attacker with local write access to the temp directory to force the tmp package to chmod or delete arbitrary files by pre-placing a symlink before the package's cleanup routine ran, and the fix required moving to fs.mkdtemp with cryptographically random suffixes rather than predictable counters. These are not obscure, theoretical findings — they are documented CVEs against dependencies that sit in the build path of enterprise software shipped today, which is exactly why insecure temp file handling is a software supply chain concern, not just an application security footnote.

How Does an Attacker Exploit a Predictable Temp File?

An attacker exploits it by winning a race between your process's file-naming step and its file-creation step, using a symlink or pre-created file to redirect the outcome. The typical sequence is: your application (often running with elevated privileges, such as root or a service account) generates a temp filename based on a predictable pattern — a process ID, a timestamp, or a simple incrementing counter. The attacker, who has write access to the same shared temp directory, monitors for that naming pattern or predicts it outright, then creates a symbolic link at that exact path pointing to a sensitive target file, such as /etc/shadow, a configuration file, or another user's data. When your privileged process opens "its" temp file and writes to it — believing it created a fresh, private file — it is actually following the symlink and overwriting or corrupting the attacker's chosen target. A variant of this attack targets deletion rather than writes: if cleanup logic blindly removes a path by name, an attacker can substitute a symlink so the cleanup routine deletes an arbitrary file elsewhere on the filesystem instead.

How Can Developers Prevent Insecure Temp File Creation?

Developers prevent it by using atomic, exclusive file-creation APIs instead of separate name-then-open steps, and by scoping temp storage to a directory only the current user can access. The concrete fix is to use functions that combine name generation and file creation into a single atomic system call with the O_CREAT | O_EXCL flags — mkstemp() in C, tempfile.NamedTemporaryFile() or tempfile.mkstemp() in Python (never mktemp()), and fs.mkdtemp() with a random suffix in Node.js — so that if the target path already exists, the call fails instead of silently following a symlink. Temp files should be created with restrictive permissions (0600, not the default 0666 minus umask) from the moment they are created, not chmod'd afterward, since a race window exists in between. Where possible, use a per-user or per-process temp directory rather than a shared world-writable one, and prefer OS-provided secure temp directory APIs over hardcoding /tmp. For dependency risk, treat temp-file-handling libraries as a supply chain concern: pin versions, monitor advisories for packages like tmp, and include CWE-377 in the checklist for any code review touching file I/O, especially in code paths that run with elevated privileges or in shared CI environments.

How Safeguard Helps

Safeguard treats insecure temporary file creation as a software supply chain signal, not just a single-repository code smell, because a race condition buried three dependencies deep can compromise every downstream consumer that vendors it. Our static analysis engine flags CWE-377 patterns — calls to deprecated APIs like mktemp(), tmpnam(), and tempnam(), along with hand-rolled name-then-open sequences — directly in pull requests, before the code merges, with the specific line and the atomic-API replacement suggested inline. Because these vulnerabilities so often live in transitive dependencies rather than first-party code, Safeguard's software composition analysis cross-references your dependency tree against known CVEs like CVE-2022-24707 and CVE-2020-15250, alerting you when a package your build already trusts has a disclosed temp-file race condition, and tracking whether your pinned version actually includes the fix. For build and CI environments specifically, Safeguard's supply chain monitoring watches for shared, world-writable temp paths in build configurations and flags privileged build steps that write to predictable filenames, closing the exact gap that turned JUnit4's test helper into a fleet-wide information disclosure risk. The goal is straightforward: catch the thirty-year-old bug class before it ships, wherever in the dependency graph it happens to be hiding.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.