fix(runners): preserve stopped status when Run returns error#3981
Open
cursor[bot] wants to merge 1 commit into
Open
fix(runners): preserve stopped status when Run returns error#3981cursor[bot] wants to merge 1 commit into
cursor[bot] wants to merge 1 commit into
Conversation
Commit 2a156b3 moved the IsFinished() early-return into the err==nil branch only. When a user stops a task, checkNewJobs can move the runner job to stopped before Executor.Run returns an error from the killed process; the error path then overwrote stopped with failed. Restore the finished-status guard before error handling and add regression tests for handleJobRunComplete. Co-authored-by: Denis Gukov <fiftin@outlook.com>
Author
There was a problem hiding this comment.
Security review
Outcome: No medium, high, or critical vulnerabilities found.
This PR refactors runner job completion into handleJobRunComplete and adds an early return when the job status is already terminal (stopped, success, or error). The change fixes a correctness bug where a legitimately stopped task could be overwritten as error when Executor.Run returned a non-nil error (e.g. process killed after user stop).
Reviewed attack paths (none exploitable):
- Status spoofing / alert bypass: Terminal status is only preserved when the runner-side status is already finished before
handleJobRunCompleteruns. That state is set by authorized server stop/kill flows (checkNewJobs,applyTerminatedJobs) or executor kill handling—not by unauthenticated input in this diff. A compromised runner could already report arbitrary statuses viasendProgress; this change does not widen that trust boundary. - Authz bypass: Server-side progress handling (
api/runners/runners.go) still validates runner token/assignment, rejects invalid statuses, and ignores progress for tasks already terminal on the server. - Injection / secret leakage: No new attacker-controlled sinks. Logging uses existing
task_idfields and internalRun()errors; user-facing logs remain a static message.
No new inline findings.
Sent by Cursor Automation: Find vulnerabilities
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Bug and impact
User-stopped runner tasks could be reported as failed instead of stopped.
When a user stops a running task, the server moves it to
stopping/stoppedand the runner'scheckNewJobsloop applies that status locally and kills the executor process.Executor.Run()then returns a non-nil error (e.g. killed subprocess). Commit2a156b30(fix(runners): logs) moved theIsFinished()early-return into theerr == nilbranch only, so the error path overwrote an already-finishedstoppedstatus withfailed.This is user-facing breakage: stopped tasks appear failed in history, trigger failure notifications/workflows, and pollute metrics.
Root cause
In
services/runners/job_pool.go, post-Run()handling only checkedgetStatus().IsFinished()on the success path. The error path unconditionally setTaskFailStatusunless the status was stillTaskStoppingStatus— missing the case where the status had already advanced toTaskStoppedStatus.Fix and validation
handleJobRunCompleteand restore theIsFinished()guard before branching onerr, so a terminal status set by a concurrent stop/kill is never overwritten.2a156b30(error log only on genuine launch failures, debug log on normal completion).