Tighten RSBK package-format validation for review remediation

Reject NUL/control bytes and Windows-hostile names in entry names, relax
the over-broad ".." substring ban to component-only, close the
trailing-garbage gap on empty manifests, and relocate the package
CMake subdirectory to its ladder home.
This commit is contained in:
2026-08-02 07:56:45 -04:00
parent 043558a54d
commit e0b4ec2e21
10 changed files with 161 additions and 56 deletions
+19 -2
View File
@@ -102,13 +102,14 @@ static void testEmptyManifestRoundTrips() {
// --- forward compatibility ---------------------------------------------------
static void testUnknownKeysSkippedAtEveryLevel() {
// A future additive manifest: unknown keys at the root and inside an entry.
// A future additive manifest: unknown keys at the root, inside an entry,
// and inside the nested index blob itself.
const std::string json =
"{\"bankName\":\"B\",\"exported\":7,"
"\"instrumentState\":{\"nested\":[1,2,{\"x\":\"y\"}]},"
"\"entries\":[{\"name\":\"a.wav\",\"length\":10,\"hash\":\"h\","
"\"futureField\":\"ignored\","
"\"index\":{\"version\":1,\"samples\":[{\"id\":\"s1\","
"\"index\":{\"version\":1,\"futureIndexField\":42,\"samples\":[{\"id\":\"s1\","
"\"relativePath\":\"bank/a.wav\"}]}}],"
"\"slots\":[],\"trailingUnknown\":null}";
auto m = deserializeManifest(json);
@@ -159,6 +160,17 @@ static void testDuplicateEntryNamesRejectedBothWays() {
CHECK(!deserializeManifest(dup).has_value());
}
static void testDuplicateEntriesKeyRejected() {
// A repeated "entries" key must not accumulate into two arrays' worth of
// entries — reject rather than silently union them.
const std::string json =
"{\"entries\":[{\"name\":\"a.wav\",\"length\":1,\"hash\":\"h\","
"\"index\":{\"version\":1,\"samples\":[{\"id\":\"s1\",\"relativePath\":\"p\"}]}}],"
"\"entries\":[{\"name\":\"b.wav\",\"length\":1,\"hash\":\"h\","
"\"index\":{\"version\":1,\"samples\":[{\"id\":\"s2\",\"relativePath\":\"q\"}]}}]}";
CHECK(!deserializeManifest(json).has_value());
}
// --- rejection: structural ---------------------------------------------------
static void testEncodeRejectsUnrepresentableSample() {
@@ -223,6 +235,10 @@ static void testDecodeRejectsMalformedShapes() {
auto json = serializeManifest(fixture());
CHECK(json.has_value());
CHECK(!deserializeManifest(*json + "x").has_value());
// Trailing garbage after the EMPTY-object shortcut specifically: this path
// returned early before reaching the eof check, so "{}JUNK" parsed valid.
CHECK(!deserializeManifest("{}JUNK").has_value());
CHECK(deserializeManifest("{}").has_value());
// Truncation at a few JSON-level offsets (byte-level truncation of the whole
// package is bank_package's suite).
CHECK(!deserializeManifest(json->substr(0, json->size() / 2)).has_value());
@@ -236,6 +252,7 @@ int main() {
testEncodeRejectsBadEntryName();
testDecodeRejectsBadEntryName();
testDuplicateEntryNamesRejectedBothWays();
testDuplicateEntriesKeyRejected();
testEncodeRejectsUnrepresentableSample();
testDecodeRejectsMalformedShapes();