Skip to content

VNX-281: Improper Preservation of Permissions

Detects a C/C++ file replaced through a temporary (rename over the original) whose creation mode is the default or group/other-accessible and never copied from the original's st_mode, so a restricted file comes back readable by others.

Updated 02 Nov 2026Checked against cli@de9b52a
Field Value
Rule ID VNX-281
Severity Low
SARIF level note
Kind sast
Languages C, C++
CWE CWE-281
CAPEC CAPEC-66
ATT&CK T1190
CWSS vector CWSS:1.0/TI:L/AP:RU/AL:S/IC:N/FC:LT/RP:RU/RL:S/AV:L/AS:N/IN:A/SC:A/BI:L/DI:H/EX:L/EC:N/P:L
Tags improper-preservation-permissions file-permissions cwe-281
Source internal/sast/rules/vnx-281.rego

Overview

VNX-281 maps to CWE-281: Improper Preservation of Permissions. Rewriting a file safely means writing a temporary and renaming it over the original. The rename replaces the inode, so the new file has whatever mode the temporary was created with, not the mode the administrator gave the original. A 0600 credentials file, ACL or key store rewritten this way comes back 0644 (or 0666 & ~umask).

The rule reports rename(), std::rename() or std::filesystem::rename() of a temporary created in the 60 lines above with:

  • fopen(tmp, "w")/"a" or a std::ofstream/fstream (default mode),
  • open(tmp, ...O_CREAT..., 0644) or creat(tmp, 0666) (group/other bits),
  • or an owner-only temporary (mkstemp, open(..., 0600)) widened by a literal chmod/fchmod such as fchmod(fd, 0644),

when nothing nearby reads the original’s st_mode or applies a mode held in a variable.

What Gets Flagged

FILE *out = fopen(tmp, "w");
fputs(body, out);
fclose(out);
if (rename(tmp, path) != 0)   /* FLAGGED: path loses its mode */
    return -1;
int fd = mkstemp(tmpname);
write(fd, buf, n);
fchmod(fd, 0644);             /* widened to a fixed mode */
rename(tmpname, aclfile);     /* FLAGGED */

What Is Not Flagged

  • A temporary given the original’s mode: fchmod(fileno(out), st.st_mode & 07777), chmod(tmp, original_mode).
  • An owner-only temporary (mkstemp, open(..., 0600)) that is never widened.
  • rename() that moves an existing file, and writing a new file without replacing one.
  • Comments and string literals.

Whether the replaced file was ever more restrictive than the new mode is decided at run time and is not judged.

Remediation

stat() the original before writing, create the temporary owner-only with mkstemp(), then apply the original’s mode (and owner, when running as root) before the rename:

struct stat st;
if (stat(path, &st) != 0) return -1;
int fd = mkstemp(tmpname);
/* ... write ... */
if (fchmod(fd, st.st_mode & 07777) != 0) return -1;
fsync(fd);
close(fd);
return rename(tmpname, path);

References

Suppressing this rule

Put a nosec comment on the line, or on line 1 to cover the whole file. Use the comment marker of the file’s language (#, //, -- or ;):

# nosec vnx-281
// nosec vnx-281

Or record an ignore rule: vulnetix ignore add --rule vnx-281 --reason "...". See Suppressing findings.

Edit this pageSources: cli/internal/sast/rules/vnx-281.rego, cli/internal/sast/docs/vnx-281.md
esc
↑ ↓ move↵ openSearches in your browser. A key like autonomy.max or a code like threshold_required jumps straight to it.