fix: close STEP-078 V4 bridge guard audit
Some checks failed
validate-scripts / validate-scripts (push) Has been cancelled
Some checks failed
validate-scripts / validate-scripts (push) Has been cancelled
This commit is contained in:
@@ -37,6 +37,7 @@
|
||||
#include <mbgl/style/filter.hpp>
|
||||
#include <mbgl/renderer/query.hpp>
|
||||
#include <mbgl/particle/field/field_profile_contract_json.hpp>
|
||||
#include <mbgl/particle/ingress/encoded_field_transport.hpp>
|
||||
|
||||
// Java -> C++ conversion
|
||||
#include "style/android_conversion.hpp"
|
||||
@@ -1425,7 +1426,7 @@ jni::jboolean NativeMapView::controlRasterArraySourceTimeline(JNIEnv& env,
|
||||
jni::jint NativeMapView::submitRasterArraySourceEncodedField(JNIEnv& env, const jni::String& sourceId, const jni::String& fieldKey, const jni::String& fieldId, jni::jlong generation, jni::jlong dataVersion, const jni::String& contractJson, const jni::Array<jbyte>& qbin) {
|
||||
const auto id=jni::Make<std::string>(env,sourceId); auto* source=dynamic_cast<mbgl::style::RasterArraySource*>(map->getStyle().getSource(id));
|
||||
if(!source) return 0; // bridge-layer SOURCE_MISSING sentinel, never a Core result
|
||||
if(generation<=0 || dataVersion<0 || !qbin) return static_cast<jni::jint>(mbgl::style::RasterArrayEncodedFieldSubmitResult::RejectedDecode);
|
||||
if(generation<=0 || dataVersion<0 || !qbin || !mbgl::particle::ingress::isValidEncodedFieldTransportSize(qbin.Length(env))) return static_cast<jni::jint>(mbgl::style::RasterArrayEncodedFieldSubmitResult::RejectedDecode);
|
||||
const auto contract=mbgl::particle::parseResolvedFieldProfileContractJson(jni::Make<std::string>(env,contractJson));
|
||||
if(!contract) return static_cast<jni::jint>(mbgl::style::RasterArrayEncodedFieldSubmitResult::RejectedContract);
|
||||
std::vector<uint8_t> bytes(qbin.Length(env)); if(!bytes.empty()) jni::GetArrayRegion(env,*qbin,0,bytes.size(),reinterpret_cast<jbyte*>(bytes.data()));
|
||||
|
||||
@@ -7,6 +7,8 @@ import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
import java.util.List;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* Main-thread, low-frequency Host facade for one raster-array weather runtime.
|
||||
@@ -86,20 +88,28 @@ public final class WeatherRuntime {
|
||||
/** Isolated V4 ingress: it deliberately does not mutate V3 state/cadence/timeline. */
|
||||
public EncodedFieldSubmitResult submitEncodedField(@NonNull String fieldKey, @NonNull String fieldId, long generation, long dataVersion, @NonNull String contractJson, @NonNull byte[] qbin) {
|
||||
requireAttached(); requireEnabled();
|
||||
if (fieldKey.isEmpty() || fieldId.isEmpty() || generation <= 0 || dataVersion < 0 || contractJson.isEmpty() || qbin.length == 0) throw new IllegalArgumentException("invalid encoded field argument");
|
||||
if (!WeatherRuntimeV4Guards.hasValidEncodedIdUtf8Length(fieldKey) || !WeatherRuntimeV4Guards.hasValidEncodedIdUtf8Length(fieldId) || !WeatherRuntimeV4Guards.hasValidEncodedContractUtf8Length(contractJson) || !hasValidEncodedContractProfileId(contractJson) || generation <= 0 || dataVersion < 0 || !WeatherRuntimeV4Guards.hasValidEncodedQbin(qbin)) throw new IllegalArgumentException("invalid encoded field argument");
|
||||
int code = map.submitRasterArraySourceEncodedField(config.sourceId, fieldKey, fieldId, generation, dataVersion, contractJson, qbin);
|
||||
if (code == 0) throw new IllegalStateException("SOURCE_MISSING");
|
||||
return EncodedFieldSubmitResult.fromCode(code);
|
||||
}
|
||||
public CommandResult clearEncodedField(@NonNull String fieldKey) { requireAttached(); return fieldKey.isEmpty() ? CommandResult.REJECTED : encodedCommand(map.clearRasterArraySourceEncodedField(config.sourceId, fieldKey)); }
|
||||
private static boolean hasValidEncodedContractProfileId(String contractJson) {
|
||||
if (contractJson == null) return false;
|
||||
try {
|
||||
return WeatherRuntimeV4Guards.hasValidEncodedIdUtf8Length(new JSONObject(contractJson).optString("profileID", ""));
|
||||
} catch (JSONException exception) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public CommandResult clearEncodedField(@NonNull String fieldKey) { requireAttached(); return !WeatherRuntimeV4Guards.hasValidEncodedIdUtf8Length(fieldKey) ? CommandResult.REJECTED : encodedCommand(map.clearRasterArraySourceEncodedField(config.sourceId, fieldKey)); }
|
||||
public CommandResult clearAllEncodedFields() { requireAttached(); return encodedCommand(map.clearAllRasterArraySourceEncodedFields(config.sourceId)); }
|
||||
/** V4 binding intent; unit and all mode selectors reach Core unchanged. */
|
||||
public CommandResult setEncodedColorBinding(@NonNull String fieldKey, int semanticID, int groupID, int valueClass, int expectedUnitID) {
|
||||
requireAttached(); return encodedCommand(map.setRasterArraySourceEncodedColorBinding(config.sourceId, fieldKey, semanticID, groupID, valueClass, expectedUnitID));
|
||||
requireAttached(); return !WeatherRuntimeV4Guards.hasValidEncodedIdUtf8Length(fieldKey) ? CommandResult.REJECTED : encodedCommand(map.setRasterArraySourceEncodedColorBinding(config.sourceId, fieldKey, semanticID, groupID, valueClass, expectedUnitID));
|
||||
}
|
||||
/** ABI reserves all selector slots: inactive slots must be exactly (0, 0). */
|
||||
public CommandResult setEncodedParticleBinding(@NonNull String fieldKey, int mode, int uSemanticID, int uGroupID, int vSemanticID, int vGroupID, int directionSemanticID, int directionGroupID, int expectedUnitID) {
|
||||
requireAttached(); return encodedCommand(map.setRasterArraySourceEncodedParticleBinding(config.sourceId, fieldKey, mode, uSemanticID, uGroupID, vSemanticID, vGroupID, directionSemanticID, directionGroupID, expectedUnitID));
|
||||
requireAttached(); return !WeatherRuntimeV4Guards.hasValidEncodedIdUtf8Length(fieldKey) ? CommandResult.REJECTED : encodedCommand(map.setRasterArraySourceEncodedParticleBinding(config.sourceId, fieldKey, mode, uSemanticID, uGroupID, vSemanticID, vGroupID, directionSemanticID, directionGroupID, expectedUnitID));
|
||||
}
|
||||
public CommandResult setEncodedForecastTime(long timestamp) { requireAttached(); return timestamp < 0 ? CommandResult.REJECTED : encodedCommand(map.setRasterArraySourceEncodedForecastTime(config.sourceId, timestamp)); }
|
||||
/** State and speed intentionally remain independent V4 Core commands. */
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package org.maplibre.android.maps;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/** Small transport guards shared by Java facade checks; they do not define QBIN decoding. */
|
||||
final class WeatherRuntimeV4Guards {
|
||||
static final int MAX_ENCODED_ID_UTF8_BYTES = 64;
|
||||
static final int MAX_CONTRACT_UTF8_BYTES = 256 * 1024;
|
||||
static final int MAX_ENCODED_QBIN_BYTES = 64 * 1024 * 1024;
|
||||
|
||||
private WeatherRuntimeV4Guards() {}
|
||||
|
||||
static boolean hasValidEncodedIdUtf8Length(String value) {
|
||||
if (value == null) return false;
|
||||
final int length = value.getBytes(StandardCharsets.UTF_8).length;
|
||||
return length >= 1 && length <= MAX_ENCODED_ID_UTF8_BYTES;
|
||||
}
|
||||
|
||||
static boolean hasValidEncodedQbin(byte[] qbin) {
|
||||
return qbin != null && qbin.length >= 1 && qbin.length <= MAX_ENCODED_QBIN_BYTES;
|
||||
}
|
||||
|
||||
static boolean hasValidEncodedContractUtf8Length(String contractJson) {
|
||||
return contractJson != null && !contractJson.isEmpty()
|
||||
&& contractJson.getBytes(StandardCharsets.UTF_8).length <= MAX_CONTRACT_UTF8_BYTES;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.maplibre.android.maps;
|
||||
|
||||
/** JDK-only directed guard test; Android facade compilation remains a later Android validation. */
|
||||
public final class WeatherRuntimeV4GuardsTest {
|
||||
public static void main(String[] args) {
|
||||
require(WeatherRuntimeV4Guards.hasValidEncodedIdUtf8Length(repeat("a", 64)));
|
||||
require(!WeatherRuntimeV4Guards.hasValidEncodedIdUtf8Length(repeat("风", 33)));
|
||||
require(WeatherRuntimeV4Guards.hasValidEncodedQbin(new byte[64 * 1024 * 1024]));
|
||||
require(!WeatherRuntimeV4Guards.hasValidEncodedQbin(new byte[0]));
|
||||
require(!WeatherRuntimeV4Guards.hasValidEncodedQbin(new byte[64 * 1024 * 1024 + 1]));
|
||||
require(WeatherRuntimeV4Guards.hasValidEncodedContractUtf8Length("{}"));
|
||||
require(!WeatherRuntimeV4Guards.hasValidEncodedContractUtf8Length("a".repeat(256 * 1024 + 1)));
|
||||
}
|
||||
|
||||
private static String repeat(String value, int count) { return value.repeat(count); }
|
||||
private static void require(boolean value) { if (!value) throw new AssertionError("V4 transport guard failed"); }
|
||||
}
|
||||
Reference in New Issue
Block a user