fix: resolve all remaining audit failures across rust, svelte, and java

This commit is contained in:
scoped committed 2026-09-29 15:51:15 -04:00
1 parent bc12a25a01
commit 590178e41a
35 files changed
+958 -300

No files matched your search

@@ -9,7 +9,7 @@ import java.util.function.*;
public final class Integration implements AutoCloseable {
public static final String UNAVAILABLE = "SCOPENET sign-in is unavailable. Please try again shortly.";
public final Activity activity = new Activity();
private final Settings settings;
private volatile Settings settings;
private final PanelClient client;
private final JsonObject hello;
private final Consumer<String> log;
@@ -115,6 +115,11 @@ public final class Integration implements AutoCloseable {
}
}
public void reload(Settings next) {
client.updateSettings(next);
settings = next;
}
public Settings settings() {
return settings;
}
@@ -129,6 +134,7 @@ public final class Integration implements AutoCloseable {
@Override public void close() {
closed = true;
client.close();
loginWorker.shutdownNow();
activity.leaveAll();
Future<?> flush = syncWorker.submit(() -> {
@@ -7,7 +7,23 @@ import java.net.http.*;
import java.time.Duration;
public final class PanelClient {
private final Settings settings;
private volatile Settings settings;
private record ChunkKey(String dimension, int x, int z, java.util.UUID uuid) {}
private record Cached(ChunkCheckResult result, long expires) {}
private final java.util.Map<ChunkKey, Cached> claims = new java.util.concurrent.ConcurrentHashMap<>();
private final java.util.Set<ChunkKey> pendingClaims = java.util.concurrent.ConcurrentHashMap.newKeySet();
private final java.util.concurrent.ThreadPoolExecutor claimWorker = new java.util.concurrent.ThreadPoolExecutor(
2, 2, 0, java.util.concurrent.TimeUnit.SECONDS, new java.util.concurrent.ArrayBlockingQueue<>(64),
task -> { Thread t = new Thread(task, "scopenet-claims"); t.setDaemon(true); return t; });
private static final ChunkCheckResult UNKNOWN = new ChunkCheckResult(true, false, "Protection check pending or unavailable", "WAIT");
private final java.util.concurrent.atomic.AtomicLong claimGeneration = new java.util.concurrent.atomic.AtomicLong();
public void invalidateClaims() { claimGeneration.incrementAndGet(); claims.clear(); }
public void updateSettings(Settings next) { settings = next; invalidateClaims(); }
public void close() { claimWorker.shutdownNow(); }
public static class HttpFailure extends IOException {
public final int status;
public HttpFailure(int status, String message) { super(message); this.status = status; }
}
private final HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(3))
.followRedirects(HttpClient.Redirect.NEVER).build();
@@ -28,7 +44,7 @@ public final class PanelClient {
if (err.has("message")) msg = err.get("message").getAsString();
else if (err.has("error")) msg = err.get("error").getAsString();
} catch (Exception ignored) {}
throw new IOException(msg);
throw new HttpFailure(response.statusCode(), msg);
}
try {
return JsonParser.parseString(response.body());
@@ -53,6 +69,24 @@ public final class PanelClient {
}
public ChunkCheckResult checkChunk(String dimension, int chunkX, int chunkZ, java.util.UUID uuid) {
if (!settings.guildsEnabled() || !settings.landClaimingEnabled()) return new ChunkCheckResult(false, true, null, null);
ChunkKey key = new ChunkKey(dimension, chunkX, chunkZ, uuid);
Cached cached = claims.get(key);
if (cached != null && cached.expires() > System.nanoTime()) return cached.result();
if (pendingClaims.add(key)) {
long generation = claimGeneration.get();
try { claimWorker.execute(() -> {
try {
ChunkCheckResult result = checkChunkRemote(dimension, chunkX, chunkZ, uuid);
if (claims.size() > 4096) claims.clear();
if (generation == claimGeneration.get()) claims.put(key, new Cached(result, System.nanoTime() + java.util.concurrent.TimeUnit.SECONDS.toNanos(2)));
} finally { pendingClaims.remove(key); }
}); } catch (java.util.concurrent.RejectedExecutionException e) { pendingClaims.remove(key); }
}
return UNKNOWN;
}
private ChunkCheckResult checkChunkRemote(String dimension, int chunkX, int chunkZ, java.util.UUID uuid) {
if (!settings.guildsEnabled() || !settings.landClaimingEnabled()) {
return new ChunkCheckResult(false, true, null, null);
}
@@ -64,12 +98,12 @@ public final class PanelClient {
try {
JsonObject res = post("guilds/check-chunk", req);
boolean claimed = res.has("claimed") && res.get("claimed").getAsBoolean();
boolean allowed = !res.has("allowed") || res.get("allowed").getAsBoolean();
boolean allowed = res.has("allowed") && res.get("allowed").getAsBoolean();
String guildName = res.has("guild_name") && !res.get("guild_name").isJsonNull() ? res.get("guild_name").getAsString() : null;
String guildTag = res.has("guild_tag") && !res.get("guild_tag").isJsonNull() ? res.get("guild_tag").getAsString() : null;
return new ChunkCheckResult(claimed, allowed, guildName, guildTag);
} catch (Exception e) {
return new ChunkCheckResult(false, true, null, null);
return UNKNOWN;
}
}
@@ -79,7 +113,9 @@ public final class PanelClient {
req.addProperty("dimension", dimension);
req.addProperty("chunk_x", chunkX);
req.addProperty("chunk_z", chunkZ);
return post("guilds/claim", req);
JsonObject result = post("guilds/claim", req);
invalidateClaims();
return result;
}
public JsonObject unclaimChunk(String dimension, int chunkX, int chunkZ, java.util.UUID uuid) throws IOException, InterruptedException {
@@ -88,7 +124,9 @@ public final class PanelClient {
req.addProperty("dimension", dimension);
req.addProperty("chunk_x", chunkX);
req.addProperty("chunk_z", chunkZ);
return post("guilds/unclaim", req);
JsonObject result = post("guilds/unclaim", req);
invalidateClaims();
return result;
}
public JsonObject getPlayerGuild(java.util.UUID uuid) throws IOException, InterruptedException {
@@ -147,6 +185,7 @@ public final class PanelClient {
public JsonObject marketList(java.util.UUID sellerUuid, String sellerName, String itemId, String itemName, int amount, double price) throws IOException, InterruptedException {
JsonObject req = new JsonObject();
req.addProperty("operation_id", java.util.UUID.randomUUID().toString());
req.addProperty("seller_uuid", sellerUuid.toString());
req.addProperty("seller_name", sellerName);
req.addProperty("item_id", itemId);
@@ -158,10 +197,33 @@ public final class PanelClient {
public JsonObject marketBuy(long listingId, java.util.UUID buyerUuid, String buyerName) throws IOException, InterruptedException {
JsonObject req = new JsonObject();
req.addProperty("operation_id", java.util.UUID.randomUUID().toString());
req.addProperty("listing_id", listingId);
req.addProperty("buyer_uuid", buyerUuid.toString());
req.addProperty("buyer_name", buyerName);
return post("economy/market/buy", req);
}
/**
* Atomically add (or subtract, if amount is negative) to a player's balance.
* Returns the new balance. This avoids the read-modify-write race in the old
* getBalance + syncBalance pattern.
*/
public double addBalance(java.util.UUID uuid, String username, double amount, String description) throws IOException, InterruptedException {
JsonObject req = new JsonObject();
req.addProperty("operation_id", java.util.UUID.randomUUID().toString());
req.addProperty("uuid", uuid.toString());
req.addProperty("username", username);
req.addProperty("delta", amount);
req.addProperty("description", description);
JsonObject res = post("economy/adjust", req);
return res.has("balance") ? res.get("balance").getAsDouble() : 0.0;
}
/** Fetch active player marketplace listings. */
public JsonArray getMarketListings() throws IOException, InterruptedException {
JsonElement el = postElement("economy/market", new JsonObject());
return el.isJsonArray() ? el.getAsJsonArray() : new JsonArray();
}
}