spring-projects / spring-projects/spring-security

DelegatingSecurityContextScheduledExecutorService causes silent task termination when delegate throws exception

Open
#18,389 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

status: waiting-for-triage type: bug
Dominant language
Java
Stars
9.6k
Forks
6.3k
Avg merge
2d 11h
Merged PRs (30d)
52

Description

Describe the bug
When using DelegatingSecurityContextScheduledExecutorService to wrap a ScheduledExecutorService, any exception thrown by the delegate task propagates out of the DelegatingSecurityContextRunnable wrapper.

According to the ScheduledExecutorService contract (specifically scheduleAtFixedRate and scheduleWithFixedDelay), if any execution of the task encounters an exception, subsequent executions are suppressed.

Because DelegatingSecurityContextRunnable.run() uses a try-finally block (to clear the context) but does not catch exceptions, a single transient error in a secured scheduled task causes the entire schedule to silently stop forever. No error log is produced by default, leading to a "Silent Failure" (ENC - Exception Not Caught pattern).

To Reproduce
Steps to reproduce the behavior:

  1. Create a standard ScheduledExecutorService.
  2. Wrap it with DelegatingSecurityContextScheduledExecutorService.
  3. Schedule a task that throws a RuntimeException.
  4. Observe that the task runs once and then never runs again.

Expected behavior
While DelegatingSecurityContextRunnable correctly prioritizes transparency for standard execution, the Scheduled implementation (DelegatingSecurityContextScheduledExecutorService) should arguably provide a mechanism to trap/log exceptions or ensuring that the security wrapper doesn't inadvertently contribute to the "silent death" of scheduled tasks.

At a minimum, documentation should warn that wrapped scheduled tasks must handle all exceptions internally.

Sample

import org.springframework.security.concurrent.DelegatingSecurityContextScheduledExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class SecuritySchedulerBug {
    public static void main(String[] args) throws InterruptedException {
        ScheduledExecutorService delegate = Executors.newScheduledThreadPool(1);
        ScheduledExecutorService securityExecutor = new DelegatingSecurityContextScheduledExecutorService(delegate);

        System.out.println("Starting task...");

        securityExecutor.scheduleAtFixedRate(() -> {
            System.out.println("Executing task logic...");
            // Simulate a business logic error
            throw new RuntimeException("Transient Error");
        }, 0, 1, TimeUnit.SECONDS);

        Thread.sleep(5000);
        // Result: Task prints "Executing..." ONCE, then silence.
        // The schedule is dead.
    }
}

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with DelegatingSecurityContextScheduledExecutorService.scheduleAtFixedRate and scheduleWithFixedDelay, then inspect DelegatingSecurityContextRunnable.run() and the ScheduledExecutorService contract. Reproduce the one-run suppression described in the issue and clarify whether the intended outcome is changed exception handling or a documentation warning; completion should include the chosen behavior and corresponding verification.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, spring
Domain
security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
40/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.