forked from weaselab/weaseljson
schemagen: reject duplicate unknown keys in non-strict objects
Track unknown keys in a per-object unordered_set so that permissive objects (additionalProperties absent/true) still reject duplicate keys, matching the README guarantee. - Add std::unordered_set<std::string> to Frame. - Insert unknown keys in cbKeyData and reject duplicates before skipping. - Add a permissive "loose" subobject to example.schema.json. - Test single unknown key accepted and duplicate unknown/known keys rejected.
This commit is contained in:
@@ -52,6 +52,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"loose": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"known": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"friends": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
|
||||
@@ -186,6 +186,20 @@ int main() {
|
||||
expectReject(R"([1,2,3])", "array where object expected (root)");
|
||||
expectReject(R"({"name":"x","age":1,)", "truncated / invalid json");
|
||||
|
||||
// ---- duplicate keys in permissive (additionalProperties allowed) object
|
||||
// ----
|
||||
{
|
||||
RootBuilder b;
|
||||
WeaselJsonStatus s =
|
||||
parseStrided(b, R"({"name":"x","age":1,"loose":{"unknown":1}})");
|
||||
CHECK(s == WeaselJson_OK);
|
||||
printf("ok single unknown key in non-strict object\n");
|
||||
}
|
||||
expectReject(R"({"name":"x","age":1,"loose":{"unknown":1,"unknown":2}})",
|
||||
"duplicate unknown key in non-strict object");
|
||||
expectReject(R"({"name":"x","age":1,"loose":{"known":"a","known":"b"}})",
|
||||
"duplicate known key in non-strict object");
|
||||
|
||||
if (failures == 0) {
|
||||
printf("\nALL TESTS PASSED\n");
|
||||
return 0;
|
||||
|
||||
@@ -566,6 +566,7 @@ class Emitter:
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "weaseljson.h"
|
||||
@@ -931,6 +932,7 @@ private:
|
||||
void *dest;
|
||||
int field = -1; // object: selected field (-1 want key, -2 skip)
|
||||
std::vector<uint64_t> seen; // populated field bitset (object frames)
|
||||
std::unordered_set<std::string> unknown; // unknown keys seen in non-strict objects
|
||||
}};
|
||||
static constexpr int kWantKey = -1;
|
||||
static constexpr int kSkip = -2;
|
||||
@@ -1014,12 +1016,16 @@ private:
|
||||
scratch_.append(buf, len);
|
||||
if (!done) return;
|
||||
int idx = matchKey(f.kind, scratch_);
|
||||
scratch_.clear();
|
||||
if (idx < 0) {{
|
||||
if (isStrict(f.kind)) {{ reject(); return; }}
|
||||
if (!f.unknown.insert(scratch_).second) {{
|
||||
reject(); return; // duplicate unknown key
|
||||
}}
|
||||
scratch_.clear();
|
||||
f.field = kSkip;
|
||||
return;
|
||||
}}
|
||||
scratch_.clear();
|
||||
if (f.seen[idx >> 6] & (1ull << (idx & 63))) {{
|
||||
reject(); return; // duplicate key
|
||||
}}
|
||||
|
||||
Reference in New Issue
Block a user