Harden MCP OAuth fallback credential writes (#39611)

## Why

The fallback file contains OAuth credentials, so it must be private from the
moment it is created and writes must not follow links to another path.

## What changed

- Create the fallback file with mode `0600` on Unix before writing credentials.
- Reject symlinks and other non-regular files on Unix and Windows, including
  Windows reparse points.
- Truncate and update an existing regular file through the validated file
  handle, restoring private Unix permissions when needed.

## Testing

Added coverage for creation under a permissive umask, updates through an
existing hard link, permission restoration, and symlink rejection.

GitOrigin-RevId: d3e4be52e8f4cebb66c59d496a7f77a952a2d0d9
This commit is contained in:
jif authored and copyberry committed 2026-08-20 06:17:48 +00:00
1 parent c19482a768
commit 929e2b9c1d
3 files changed
+125 -7

No files matched your search

+5
View File
@@ -72,6 +72,10 @@ pretty_assertions = { workspace = true }
serial_test = { workspace = true }
tempfile = { workspace = true }
wiremock = { workspace = true }
[target.'cfg(unix)'.dependencies]
libc = { workspace = true }
[target.'cfg(target_os = "linux")'.dependencies]
keyring = { workspace = true, features = ["linux-native-async-persistent"] }
@@ -80,6 +84,7 @@ keyring = { workspace = true, features = ["apple-native"] }
[target.'cfg(target_os = "windows")'.dependencies]
keyring = { workspace = true, features = ["windows-native"] }
windows-sys = { version = "0.52", features = ["Win32_Storage_FileSystem"] }
[target.'cfg(any(target_os = "freebsd", target_os = "openbsd"))'.dependencies]
keyring = { workspace = true, features = ["sync-secret-service"] }