Skip to content

Enforce vote delay for proxy votes - #1545

Closed
BenCodez wants to merge 12 commits into
masterfrom
codex/fix-proxy-vote-delay-bypass-vulnerability
Closed

Enforce vote delay for proxy votes#1545
BenCodez wants to merge 12 commits into
masterfrom
codex/fix-proxy-vote-delay-bypass-vulnerability

Conversation

@BenCodez

@BenCodez BenCodez commented Aug 8, 2026

Copy link
Copy Markdown
Owner

Motivation

  • A newly introduced helper allowed proxy-forwarded votes to bypass WaitUntilVoteDelay when the proxy message timestamp equalled the stored last-vote time, which can be replayed or forged to gain duplicate rewards.
  • The change removes that permissive predicate so the backend consistently enforces vote-delay eligibility for proxy and direct votes.

Description

  • Remove the unsafe helper class ProxyVoteDelayCheck and its unit test ProxyVoteDelayCheckTest to eliminate the timestamp-equality bypass.
  • Update PlayerVoteListener to stop consulting the removed helper and to apply the WaitUntilVoteDelay gate uniformly (voteSite.isWaitUntilVoteDelay() + user.canVoteSite(voteSite)), rejecting ineligible votes before reward/total processing.
  • Preserve existing, intended exceptions and behavior for non-real votes, users with VotingPlugin.BypassWaitUntilVoteDelay, and configured WaitUntilVoteDelay rewards.

Testing

  • Ran repository checks including git diff --check, git show --check --stat, and searched the source for ProxyVoteDelayCheck and queuedProxyVoteAlreadyRecorded, which confirmed the helper and references were removed.
  • Verified a commit was created with message Enforce vote delay for proxy votes and the working tree is clean after the change.
  • Attempted mvn -q -DskipTests compile and mvn -q test, but both were blocked by external dependency resolution failing with HTTP 403 from Maven Central for maven-resources-plugin:3.3.1, so the project build/tests could not be executed in this environment.
  • AI disclosure: This pull request was created with assistance from OpenAI Codex and reviewed by BenCodez.

Codex Task

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a261674935

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

boolean queuedProxyVoteAlreadyRecorded = ProxyVoteDelayCheck.isQueuedVoteAlreadyRecorded(event.isBungee(),
event.getTime(), user.getTime(voteSite));
if (voteSite.isWaitUntilVoteDelay() && !queuedProxyVoteAlreadyRecorded && !user.canVoteSite(voteSite)) {
if (voteSite.isWaitUntilVoteDelay() && !user.canVoteSite(voteSite)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve delivery of already-recorded queued proxy votes

Restore the exact-timestamp exception for queued proxy votes. In single-server proxy mode, an offline vote is placed in OnlineCache and a VoteUpdate records its timestamp on the backends (VotingPluginProxy.java:1867-1878); when the player later joins, checkOnlineVotes sends that cached vote and immediately removes it (VotingPluginProxy.java:536-563). Because canVoteSite now sees the same timestamp and returns false, this condition rejects the legitimate cached vote, so its normal rewards/totals are never processed and the cache entry is lost whenever WaitUntilVoteDelay is enabled.

Useful? React with 👍 / 👎.

BenCodez commented Aug 9, 2026

Copy link
Copy Markdown
Owner Author

@codex address the unresolved P1 review feedback. Preserve legitimate delivery of already-recorded queued proxy votes without reintroducing a general timestamp-equality replay bypass: bind the exception to the actual queued-vote flow/metadata and ensure stale or forged proxy messages remain rejected. Add regression coverage for legitimate queued delivery and replay/stale cases, then run relevant checks.

@chatgpt-codex-connector

Copy link
Copy Markdown

Summary

Testing

  • git diff --check
  • git show --check --stat --oneline HEAD
  • ⚠️ mvn -q -Dtest=ProxyVoteDelayCheckTest,VotingPluginWireTest test (Maven Central returned HTTP 403 while resolving maven-resources-plugin:3.3.1)
  • ⚠️ /opt/codex/mcp/.venv/bin/python /tmp/call_make_pr.py (configured make_pr server could not import mcp.server.fastmcp)
  • ⚠️ /opt/codex/mcp/.venv/bin/pip install 'mcp[cli]' -q (environment network proxy returned HTTP 403, preventing repair of the make_pr server dependency)

View task →

BenCodez commented Aug 9, 2026

Copy link
Copy Markdown
Owner Author

Implemented the Codex finding directly: legitimate queued proxy votes with the recorded timestamp are accepted again, while direct/stale messages remain subject to the delay check. @codex review the new commit.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 👍

Reviewed commit: a0e254ad7b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link
Copy Markdown
Owner Author

@codex review the current head after the queued-proxy metadata fix and regression tests. Please verify that only identified VoteOnline deliveries receive the timestamp exception, while ordinary, stale, forged, or unidentified proxy votes remain delay-protected, and confirm whether this PR is ready.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6a71c470af

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +654 to +655
boolean identifiedQueuedDelivery = VotingPluginWire.SUB_VOTE_ONLINE.equals(msg.getSubChannel())
&& v.voteId != null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Verify queue provenance before bypassing the delay

A VoteOnline subchannel plus any non-null wire-supplied UUID does not prove that this message is the current delivery of an OnlineCache entry. After processedWireVotes expires the UUID in 30 minutes (or after a backend restart), replaying a previously accepted VoteOnline reserves the ID again; its timestamp still equals LastVotes, so this classification causes the listener to skip WaitUntilVoteDelay and process duplicate rewards/totals. A forged VoteOnline with a fresh UUID and the stored timestamp has the same result. Bind the exception to actual queue-delivery provenance rather than these forgeable fields.

Useful? React with 👍 / 👎.

Comment on lines +656 to +657
user.bungeeVotePluginMessaging(service, v.time, text, setTotalsOnBackend, v.wasOnline, v.broadcast, v.num,
identifiedQueuedDelivery);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Suppress per-server points when the delay rejects the vote

When PerServerPoints is enabled and an ordinary, stale, or unidentified proxy vote fails WaitUntilVoteDelay, bungeeVotePluginMessaging only fires the event and returns normally after PlayerVoteListener exits early. The handler then unconditionally executes the user.addPoints(...) block immediately below, so a delay-rejected message still awards points; forged messages with fresh vote IDs can repeat this even though their normal rewards and totals are blocked. Propagate whether the event was accepted and grant per-server points only after successful processing.

Useful? React with 👍 / 👎.

Comment on lines +262 to +263
public void bungeeVotePluginMessaging(String service, long time, VoteTotalsSnapshot text, boolean setTotals,
boolean wasOnline, boolean broadcast, int num, boolean queuedProxyVote) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve the existing public vote-messaging overload

Changing this public method's descriptor from seven parameters to eight removes the method that already-compiled integrations link against. The project publishes the plugin as a Maven dependency and generates API documentation, so any downstream plugin calling the former bungeeVotePluginMessaging(..., int) overload will fail with NoSuchMethodError after upgrading even though its source remains valid against the prior release. Keep the old overload and delegate it to the new method with queuedProxyVote set to false.

Useful? React with 👍 / 👎.

@BenCodez BenCodez closed this Aug 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant