From 0cf815a3bea63de672100283a15503a5dc0859d5 Mon Sep 17 00:00:00 2001
From: Nikita Tkachenko
Date: Tue, 5 Aug 2025 20:17:52 +0200
Subject: [PATCH 1/2] Do not follow symlinks by default when building
repository index
(cherry picked from commit 21661c26cd50e8b02bb1dc296ef0efdfe5b0260c)
---
.../source/index/RepoIndexBuilder.java | 17 +++--------------
.../trace/api/config/CiVisibilityConfig.java | 2 ++
.../src/main/java/datadog/trace/api/Config.java | 8 ++++++++
3 files changed, 13 insertions(+), 14 deletions(-)
diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/source/index/RepoIndexBuilder.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/source/index/RepoIndexBuilder.java
index 805cb4e56c1..1028d55f777 100644
--- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/source/index/RepoIndexBuilder.java
+++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/source/index/RepoIndexBuilder.java
@@ -104,6 +104,7 @@ private static final class RepoIndexingFileVisitor implements FileVisitor
private final RepoIndexingStats indexingStats;
private final Path repoRoot;
private final AtomicInteger sourceRootCounter;
+ private final boolean followSymlinks;
private RepoIndexingFileVisitor(
Config config,
@@ -120,29 +121,17 @@ private RepoIndexingFileVisitor(
packageTree = new PackageTree(config);
indexingStats = new RepoIndexingStats();
sourceRootCounter = new AtomicInteger();
+ followSymlinks = config.isCiVisibilityRepoIndexFollowSymlinks();
}
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
- if (Files.isSymbolicLink(dir) && readSymbolicLink(dir).startsWith(repoRoot)) {
- // The path is a symlink that points inside the repo.
- // We'll visit the folder that it points to anyway,
- // moreover, we don't want two different results for one file
- // (one containing the symlink, the other - the actual folder).
+ if (Files.isSymbolicLink(dir) && !followSymlinks) {
return FileVisitResult.SKIP_SUBTREE;
}
return FileVisitResult.CONTINUE;
}
- private static Path readSymbolicLink(Path path) {
- try {
- return Files.readSymbolicLink(path);
- } catch (Exception e) {
- log.debug("Could not read symbolic link {}", path, e);
- return path;
- }
- }
-
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
indexingStats.filesVisited++;
diff --git a/dd-trace-api/src/main/java/datadog/trace/api/config/CiVisibilityConfig.java b/dd-trace-api/src/main/java/datadog/trace/api/config/CiVisibilityConfig.java
index ad93aa9dfc7..e39832f1432 100644
--- a/dd-trace-api/src/main/java/datadog/trace/api/config/CiVisibilityConfig.java
+++ b/dd-trace-api/src/main/java/datadog/trace/api/config/CiVisibilityConfig.java
@@ -44,6 +44,8 @@ public final class CiVisibilityConfig {
"civisibility.ciprovider.integration.enabled";
public static final String CIVISIBILITY_REPO_INDEX_DUPLICATE_KEY_CHECK_ENABLED =
"civisibility.repo.index.duplicate.key.check.enabled";
+ public static final String CIVISIBILITY_REPO_INDEX_FOLLOW_SYMLINKS =
+ "civisibility.repo.index.follow.symlinks";
public static final String CIVISIBILITY_EXECUTION_SETTINGS_CACHE_SIZE =
"civisibility.execution.settings.cache.size";
public static final String CIVISIBILITY_JVM_INFO_CACHE_SIZE = "civisibility.jvm.info.cache.size";
diff --git a/internal-api/src/main/java/datadog/trace/api/Config.java b/internal-api/src/main/java/datadog/trace/api/Config.java
index 5ff002d4144..1d257c56542 100644
--- a/internal-api/src/main/java/datadog/trace/api/Config.java
+++ b/internal-api/src/main/java/datadog/trace/api/Config.java
@@ -254,6 +254,7 @@
import static datadog.trace.api.config.CiVisibilityConfig.CIVISIBILITY_REMOTE_ENV_VARS_PROVIDER_KEY;
import static datadog.trace.api.config.CiVisibilityConfig.CIVISIBILITY_REMOTE_ENV_VARS_PROVIDER_URL;
import static datadog.trace.api.config.CiVisibilityConfig.CIVISIBILITY_REPO_INDEX_DUPLICATE_KEY_CHECK_ENABLED;
+import static datadog.trace.api.config.CiVisibilityConfig.CIVISIBILITY_REPO_INDEX_FOLLOW_SYMLINKS;
import static datadog.trace.api.config.CiVisibilityConfig.CIVISIBILITY_RESOURCE_FOLDER_NAMES;
import static datadog.trace.api.config.CiVisibilityConfig.CIVISIBILITY_RUM_FLUSH_WAIT_MILLIS;
import static datadog.trace.api.config.CiVisibilityConfig.CIVISIBILITY_SCALATEST_FORK_MONITOR_ENABLED;
@@ -1001,6 +1002,7 @@ public static String getHostName() {
private final boolean ciVisibilityTestSkippingEnabled;
private final boolean ciVisibilityCiProviderIntegrationEnabled;
private final boolean ciVisibilityRepoIndexDuplicateKeyCheckEnabled;
+ private final boolean ciVisibilityRepoIndexFollowSymlinks;
private final int ciVisibilityExecutionSettingsCacheSize;
private final int ciVisibilityJvmInfoCacheSize;
private final int ciVisibilityCoverageRootPackagesLimit;
@@ -2261,6 +2263,8 @@ PROFILING_DATADOG_PROFILER_ENABLED, isDatadogProfilerSafeInCurrentEnvironment())
configProvider.getBoolean(CIVISIBILITY_CIPROVIDER_INTEGRATION_ENABLED, true);
ciVisibilityRepoIndexDuplicateKeyCheckEnabled =
configProvider.getBoolean(CIVISIBILITY_REPO_INDEX_DUPLICATE_KEY_CHECK_ENABLED, true);
+ ciVisibilityRepoIndexFollowSymlinks =
+ configProvider.getBoolean(CIVISIBILITY_REPO_INDEX_FOLLOW_SYMLINKS, false);
ciVisibilityExecutionSettingsCacheSize =
configProvider.getInteger(CIVISIBILITY_EXECUTION_SETTINGS_CACHE_SIZE, 16);
ciVisibilityJvmInfoCacheSize = configProvider.getInteger(CIVISIBILITY_JVM_INFO_CACHE_SIZE, 8);
@@ -3822,6 +3826,10 @@ public boolean isCiVisibilityRepoIndexDuplicateKeyCheckEnabled() {
return ciVisibilityRepoIndexDuplicateKeyCheckEnabled;
}
+ public boolean isCiVisibilityRepoIndexFollowSymlinks() {
+ return ciVisibilityRepoIndexFollowSymlinks;
+ }
+
public int getCiVisibilityExecutionSettingsCacheSize() {
return ciVisibilityExecutionSettingsCacheSize;
}
From 56f32103b262b49d768b8678bf6c1677b193442b Mon Sep 17 00:00:00 2001
From: Nikita Tkachenko
Date: Wed, 6 Aug 2025 11:11:48 +0200
Subject: [PATCH 2/2] Restore the check to avoid following symlinks that point
inside the repo
(cherry picked from commit c427509dfc41c5a04b7e24ecf52e04a9116d90bc)
---
.../source/index/RepoIndexBuilder.java | 23 +++++++++++++++++--
1 file changed, 21 insertions(+), 2 deletions(-)
diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/source/index/RepoIndexBuilder.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/source/index/RepoIndexBuilder.java
index 1028d55f777..23b93ec5580 100644
--- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/source/index/RepoIndexBuilder.java
+++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/source/index/RepoIndexBuilder.java
@@ -126,12 +126,31 @@ private RepoIndexingFileVisitor(
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
- if (Files.isSymbolicLink(dir) && !followSymlinks) {
- return FileVisitResult.SKIP_SUBTREE;
+ if (Files.isSymbolicLink(dir)) {
+ if (!followSymlinks) {
+ // Configured to skip symlinks
+ return FileVisitResult.SKIP_SUBTREE;
+ }
+ if (readSymbolicLink(dir).startsWith(repoRoot)) {
+ // The path is a symlink that points inside the repo.
+ // We'll visit the folder that it points to anyway,
+ // moreover, we don't want two different results for one file
+ // (one containing the symlink, the other - the actual folder).
+ return FileVisitResult.SKIP_SUBTREE;
+ }
}
return FileVisitResult.CONTINUE;
}
+ private static Path readSymbolicLink(Path path) {
+ try {
+ return Files.readSymbolicLink(path);
+ } catch (Exception e) {
+ log.debug("Could not read symbolic link {}", path, e);
+ return path;
+ }
+ }
+
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
indexingStats.filesVisited++;