ciechanowiec opened a new issue, #977:
URL: https://github.com/apache/maven-enforcer/issues/977
### Affected version
3.6.2, 3.5.0, 3.4.1
### Bug description
## Summary
`RequireFilesExist` reports an existing, readable, non-symlink file as
missing when the configured path contains `..` segments that traverse a
firmlinked directory on macOS (e.g. `/Users`). The rule's symlink heuristic
calls `Path.toRealPath()`, which throws on such paths; the resulting exception
is caught and the file is conservatively classified as a symlink, causing the
rule to fail.
## Affected versions
Reproduced with `maven-enforcer-plugin`:
- 3.6.2
- 3.5.0
- 3.4.1
## Environment
- OS: macOS (Darwin 25.3.0, APFS, `/Users` is a firmlinked directory)
- Architecture: aarch64 (Apple Silicon)
- JDK: project's `${maven.compiler.release}` (reproduced under standard
Temurin JDK)
- Maven: 3.9.x
## Steps to reproduce
1. On macOS, place a file at `/Users/<user>/projects/repo/README.adoc`.
2. From a Maven module at `/Users/<user>/projects/repo/src/a/b/pom.xml`,
configure:
```xml
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>3.6.2</version>
<executions>
<execution>
<id>require-readme</id>
<phase>validate</phase>
<goals><goal>enforce</goal></goals>
<configuration>
<rules>
<requireFilesExist>
<files>
<file>${project.basedir}/../../../README.adoc</file>
</files>
</requireFilesExist>
</rules>
</configuration>
</execution>
</executions>
</plugin>
```
3. Run `mvn enforcer:enforce@require-readme`.
### Expected
Build succeeds. The path resolves to an existing regular file (verifiable
via `ls`, `[ -f ... ]`, `Files.exists()`, and `File.exists()`).
### Actual
```
[ERROR] Rule 0: org.apache.maven.enforcer.rules.files.RequireFilesExist
failed with message:
[ERROR] Some required files are missing:
[ERROR] /Users/<user>/projects/repo/src/a/b/../../../README.adoc
```
The same rule **succeeds** when the `<file>` value is the canonical path
with `..` collapsed (`/Users/<user>/projects/repo/README.adoc`).
## Root cause
In `enforcer-rules`,
[`RequireFilesExist`](https://github.com/apache/maven-enforcer/blob/maven-enforcer-3.6.2/enforcer-rules/src/main/java/org/apache/maven/enforcer/rules/files/RequireFilesExist.java)
treats files as missing if they are symlinks:
```java
@Override
boolean checkFile(File file) {
if (file == null) {
return true;
}
return file.exists() && !isSymlink(file);
}
private static boolean isSymlink(File file) {
try {
return Files.isSymbolicLink(file.toPath())
|| !Files.isSameFile(file.toPath(), file.toPath().toRealPath());
} catch (Exception e) {
return true; // ← any exception is treated as "this is a symlink"
}
}
```
On macOS, `Path.toRealPath()` can throw `IOException` (or a JDK-internal
exception) when the input path traverses firmlinked system directories such as
`/Users` via `..` segments, even when the file itself exists, is a regular
file, and is not a symlink. The catch-all then classifies the file as a symlink
and the rule rejects it.
This is reproducible with a tiny Java snippet:
```java
Path p =
Paths.get("/Users/<user>/projects/repo/src/a/b/../../../README.adoc");
System.out.println(Files.exists(p)); // true
System.out.println(p.toFile().exists()); // true
System.out.println(Files.isSymbolicLink(p));// false
System.out.println(p.toRealPath()); // throws on affected macOS
setups
```
## Impact
Any multi-module project on macOS that uses `RequireFilesExist` with a path
expressed relative to `${project.basedir}` (e.g. checking a file at the
repository root from a nested module) will get a spurious build failure. There
is no diagnostic clue: the message says the file is missing, but it is not.
## Suggested fixes
Any one of the following would resolve the issue:
1. **Drop the `toRealPath()`-based symlink check from `RequireFilesExist`.**
`RequireFilesExist`'s contract is "this file must exist" —
symlink-vs-regular-file is arguably out of scope. If symlink rejection is
required, expose it as an opt-in property.
2. **Use `Files.isSymbolicLink(path)` only.** Treating the file as a symlink
only when `Files.isSymbolicLink()` returns `true` is sufficient for the
documented contract and avoids `toRealPath()` failures on `..`-containing paths
entirely.
3. **Normalize the path before the symlink check.** Calling
`path.toAbsolutePath().normalize()` before `toRealPath()` collapses `..`
segments and avoids the macOS-specific throw.
4. **Stop swallowing exceptions as "symlink".** If `toRealPath()` is
retained, an `IOException` should not silently flip the verdict to "missing";
either fail with the underlying error or fall back to `Files.isSymbolicLink()`.
## Workaround
Replace the rule with `maven-antrun-plugin`, which uses Ant's `<available>`
and resolves `..` correctly:
```xml
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<id>require-readme</id>
<phase>validate</phase>
<goals><goal>run</goal></goals>
<configuration>
<target>
<fail message="Required file not found:
${project.basedir}/../../../README.adoc">
<condition>
<not>
<available file="${project.basedir}/../../../README.adoc"/>
</not>
</condition>
</fail>
</target>
</configuration>
</execution>
</executions>
</plugin>
```
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]