Skip to content

feat: Add filter registration option for random teleport locations#1292

Open
vLuckyyy wants to merge 1 commit intomasterfrom
add-filter-registration-option-in-rtp-api
Open

feat: Add filter registration option for random teleport locations#1292
vLuckyyy wants to merge 1 commit intomasterfrom
add-filter-registration-option-in-rtp-api

Conversation

@vLuckyyy
Copy link
Member

@vLuckyyy vLuckyyy commented Feb 5, 2026

No description provided.

@vLuckyyy vLuckyyy requested a review from a team as a code owner February 5, 2026 20:10
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a flexible filtering system for random teleport locations, allowing external plugins to add custom validation logic via a new RandomTeleportLocationFilter interface and API methods. A critical security concern was identified: the current filter unregistration process allows any plugin to unregister any filter by name, which could be exploited to bypass security constraints. It is recommended to update the API to require the filter instance for unregistration to ensure better isolation and security. Additionally, a circular dependency between RandomTeleportServiceImpl and RandomTeleportSafeLocationService needs to be resolved, and an improvement in the filter registration logic is suggested for robustness.

Comment on lines 32 to 36
RandomTeleportSafeLocationService(
RandomTeleportSettings randomTeleportSettings,
LocationsConfiguration locationsConfiguration
LocationsConfiguration locationsConfiguration,
RandomTeleportServiceImpl randomTeleportService
) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Injecting RandomTeleportServiceImpl here introduces a circular dependency, as RandomTeleportServiceImpl already depends on RandomTeleportSafeLocationService. This is a significant design flaw that can lead to issues with object instantiation and makes the code harder to understand, maintain, and test.

To resolve this, you should break the cycle. A good approach is to pass the required data (the collection of filters) from RandomTeleportServiceImpl to the methods of RandomTeleportSafeLocationService that need it, instead of injecting the entire service.

For example, you could modify getSafeRandomLocation:

// In RandomTeleportSafeLocationService
public CompletableFuture<Location> getSafeRandomLocation(World world, RandomTeleportRadius radius, int attemptCount, Collection<RandomTeleportLocationFilter> filters) {
    // ...
    if (this.isSafeLocation(chunk, generatedLocation, filters)) {
        // ...
    }
    // ...
}

private boolean isSafeLocation(Chunk chunk, Location location, Collection<RandomTeleportLocationFilter> filters) {
    // ...
    for (RandomTeleportLocationFilter filter : filters) {
        if (!filter.isValid(location)) {
            return false;
        }
    }
    return true;
}

Then, RandomTeleportServiceImpl would pass the filters when calling the method:

// In RandomTeleportServiceImpl
@Override
public CompletableFuture<Location> getSafeRandomLocation(World world, int attemptCount) {
    // ...
    return this.safeLocationService.getSafeRandomLocation(world, radius, attemptCount, this.registeredFilters.values());
}

This refactoring would eliminate the circular dependency and also allow you to remove the package-private getRegisteredFiltersInternal() method.

Comment on lines +177 to +183
public boolean unregisterFilter(String filterName) {
if (filterName == null || filterName.isEmpty()) {
return false;
}

return this.registeredFilters.remove(filterName) != null;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

The unregisterFilter method allows any caller with access to the RandomTeleportService to remove any registered filter by its name. Since filter names are discoverable via getRegisteredFilters(), a malicious or poorly-behaved plugin can unregister filters registered by other plugins. This can lead to a bypass of critical security checks, such as those preventing teleportation into protected regions or claimed plots. It is recommended to modify the API to require the RandomTeleportLocationFilter instance itself for unregistration, ensuring that only a caller with a reference to the filter object can remove it.

Comment on lines +169 to +173
if (this.registeredFilters.containsKey(filterName)) {
throw new IllegalArgumentException("Filter with name '" + filterName + "' is already registered");
}

this.registeredFilters.put(filterName, filter);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The current implementation for checking if a filter is already registered and then adding it is not an atomic operation. While ConcurrentHashMap is thread-safe, using putIfAbsent is a more idiomatic and robust way to perform this check-then-act operation atomically. This makes the code's intent clearer and prevents potential race conditions if multiple threads try to register a filter with the same name concurrently.

Suggested change
if (this.registeredFilters.containsKey(filterName)) {
throw new IllegalArgumentException("Filter with name '" + filterName + "' is already registered");
}
this.registeredFilters.put(filterName, filter);
if (this.registeredFilters.putIfAbsent(filterName, filter) != null) {
throw new IllegalArgumentException("Filter with name '" + filterName + "' is already registered");
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant