Skip to content

Commit f533a45

Browse files
committed
Do not follow symlinks when writing PID file
Closes gh-50173
1 parent f3b8eb0 commit f533a45

2 files changed

Lines changed: 58 additions & 15 deletions

File tree

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/system/ApplicationPid.java

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@
1818

1919
import java.io.File;
2020
import java.io.FileNotFoundException;
21-
import java.io.FileWriter;
2221
import java.io.IOException;
2322
import java.nio.file.Files;
23+
import java.nio.file.LinkOption;
24+
import java.nio.file.Path;
25+
import java.nio.file.StandardOpenOption;
2426
import java.nio.file.attribute.PosixFilePermission;
2527
import java.util.Set;
2628

@@ -104,31 +106,31 @@ public String toString() {
104106
*/
105107
public void write(File file) throws IOException {
106108
Assert.state(this.pid != null, "No PID available");
107-
createParentDirectory(file);
108-
if (file.exists()) {
109-
assertCanOverwrite(file);
110-
}
111-
try (FileWriter writer = new FileWriter(file)) {
112-
writer.append(String.valueOf(this.pid));
109+
Path path = file.toPath();
110+
createParentDirectory(path);
111+
if (Files.exists(path, LinkOption.NOFOLLOW_LINKS)) {
112+
assertCanOverwrite(path);
113113
}
114+
Files.writeString(path, this.pid.toString(), StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE,
115+
LinkOption.NOFOLLOW_LINKS);
114116
}
115117

116-
private void createParentDirectory(File file) {
117-
File parent = file.getParentFile();
118+
private void createParentDirectory(Path path) throws IOException {
119+
Path parent = path.getParent();
118120
if (parent != null) {
119-
parent.mkdirs();
121+
Files.createDirectories(parent);
120122
}
121123
}
122124

123-
private void assertCanOverwrite(File file) throws IOException {
124-
if (!file.canWrite() || !canWritePosixFile(file)) {
125-
throw new FileNotFoundException(file + " (permission denied)");
125+
private void assertCanOverwrite(Path file) throws IOException {
126+
if (!Files.isWritable(file) || !canWritePosixFile(file)) {
127+
throw new FileNotFoundException(file.toString() + " (permission denied)");
126128
}
127129
}
128130

129-
private boolean canWritePosixFile(File file) throws IOException {
131+
private boolean canWritePosixFile(Path file) throws IOException {
130132
try {
131-
Set<PosixFilePermission> permissions = Files.getPosixFilePermissions(file.toPath());
133+
Set<PosixFilePermission> permissions = Files.getPosixFilePermissions(file, LinkOption.NOFOLLOW_LINKS);
132134
for (PosixFilePermission permission : WRITE_PERMISSIONS) {
133135
if (permissions.contains(permission)) {
134136
return true;

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/system/ApplicationPidTests.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,18 @@
1717
package org.springframework.boot.system;
1818

1919
import java.io.File;
20+
import java.io.IOException;
21+
import java.nio.file.Files;
22+
import java.nio.file.Path;
23+
import java.nio.file.StandardOpenOption;
2024

2125
import org.junit.jupiter.api.Test;
26+
import org.junit.jupiter.api.condition.DisabledOnOs;
27+
import org.junit.jupiter.api.condition.OS;
2228
import org.junit.jupiter.api.io.TempDir;
2329

2430
import static org.assertj.core.api.Assertions.assertThat;
31+
import static org.assertj.core.api.Assertions.assertThatIOException;
2532
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
2633
import static org.assertj.core.api.Assertions.contentOf;
2734

@@ -70,6 +77,40 @@ void writeNewPid() throws Exception {
7077
assertThat(contentOf(file)).isEqualTo("123");
7178
}
7279

80+
@Test
81+
void overwriteExistingPid() throws Exception {
82+
File file = new File(this.tempDir, "pid");
83+
new ApplicationPid(123L).write(file);
84+
assertThat(contentOf(file)).isEqualTo("123");
85+
new ApplicationPid(456L).write(file);
86+
assertThat(contentOf(file)).isEqualTo("456");
87+
}
88+
89+
@Test
90+
@DisabledOnOs(OS.WINDOWS)
91+
void whenSymlinkToNonExistentTargetExistsAtPidFileLocationWriteThrows() throws IOException {
92+
File link = new File(this.tempDir, "pid");
93+
File target = new File(this.tempDir, "target");
94+
Files.createSymbolicLink(link.toPath(), target.toPath());
95+
ApplicationPid pid = new ApplicationPid(123L);
96+
assertThatIOException().isThrownBy(() -> pid.write(link));
97+
assertThat(Files.isSymbolicLink(link.toPath())).isTrue();
98+
assertThat(target).doesNotExist();
99+
}
100+
101+
@Test
102+
@DisabledOnOs(OS.WINDOWS)
103+
void whenSymlinkToTargetExistsAtPidFileLocationWriteThrows() throws IOException {
104+
File link = new File(this.tempDir, "pid");
105+
Path target = new File(this.tempDir, "target").toPath();
106+
Files.write(target, "target".getBytes(), StandardOpenOption.CREATE_NEW);
107+
Files.createSymbolicLink(link.toPath(), target);
108+
ApplicationPid pid = new ApplicationPid(123L);
109+
assertThatIOException().isThrownBy(() -> pid.write(link));
110+
assertThat(Files.isSymbolicLink(link.toPath())).isTrue();
111+
assertThat(target).hasContent("target");
112+
}
113+
73114
@Test
74115
void toLong() {
75116
ApplicationPid pid = new ApplicationPid(123L);

0 commit comments

Comments
 (0)