zeek/testing/btest/bifs/from_json.zeek
Christian Kreibich 92c1098e97 Support table deserialization in from_json()
This allows additional data roundtripping through JSON since to_json() already
supports tables. There are some subtleties around the formatting of strings in
JSON object keys, for which this adds a bit of helper infrastructure.

This also expands the language.table test to verify the roundtrips, and adapts
bif.from_json to include a table in the test record.
2024-07-02 14:46:16 -07:00

240 lines
5.8 KiB
Text

# @TEST-EXEC: zeek -b %INPUT
# @TEST-EXEC: TEST_DIFF_CANONIFIER= btest-diff .stdout
# @TEST-EXEC: TEST_DIFF_CANONIFIER=$SCRIPTS/diff-remove-abspath btest-diff .stderr
module A;
type Color: enum {
Red = 10,
White = 20,
Blue = 30
};
type Foo: record {
hello: string;
t: bool;
f: bool;
n: count &optional;
m: count &optional; # not in input
def: count &default = 123;
i: int;
pi: double;
a: string_vec;
c1: Color;
p: port;
ti: time;
it: interval;
ad: addr;
s: subnet;
re: pattern;
su: subnet_set;
se: set[addr, port];
tbl: table[addr, port] of string;
};
event zeek_init()
{
local json = "{\"hello\":\"world\",\"t\":true,\"f\":false,\"se\":[[\"192.168.0.1\", \"80/tcp\"], [\"2001:db8::1\", \"8080/udp\"]],\"n\":null,\"i\":123,\"pi\":3.1416,\"a\":[\"1\",\"2\",\"3\",\"4\"],\"su\":[\"[aa:bb::0]/32\",\"192.168.0.0/16\"],\"c1\":\"A::Blue\",\"p\":\"1500/tcp\",\"it\":5000,\"ad\":\"127.0.0.1\",\"s\":\"[::1/128]\",\"re\":\"/a/\",\"ti\":1681652265.042767,\"tbl\":{\"[\\\"192.168.0.1\\\",\\\"80/tcp\\\"]\":\"foo\"}}";
print from_json(json, Foo);
}
@TEST-START-NEXT
# argument type mismatch
event zeek_init()
{
print from_json("[]", 10);
}
@TEST-START-NEXT
# JSON parse error
event zeek_init()
{
print from_json("{\"hel", string_vec);
}
@TEST-START-NEXT
type bool_t: bool;
type Foo: record {
a: bool;
};
# type mismatch error
event zeek_init()
{
print from_json("[]", bool_t);
print from_json("{\"a\": \"hello\"}", Foo);
}
@TEST-START-NEXT
# type unsupport error
event zeek_init()
{
print from_json("[]", table_string_of_string);
}
@TEST-START-NEXT
type port_t: port;
# additional & incorrect port formats
event zeek_init()
{
# Ports can also be given as objects:
print from_json("{\"port\":80,\"proto\":\"tcp\"}", port_t);
# These are violations:
print from_json("\"80\"", port_t);
print from_json("{}", port_t);
}
@TEST-START-NEXT
type set_t: set[int, bool];
# index type doesn't match
event zeek_init()
{
print from_json("[[1, false], [2]]", set_t);
print from_json("[[1, false], [2, 1]]", set_t);
}
@TEST-START-NEXT
type pattern_t: pattern;
# pattern compile error
event zeek_init()
{
print from_json("\"/([[:print:]]{-}[[:alnum:]]foo)/\"", pattern_t);
}
@TEST-START-NEXT
type Color: enum {
Red = 10
};
# enum error
event zeek_init()
{
print from_json("\"Yellow\"", Color);
}
@TEST-START-NEXT
# container null
event zeek_init()
{
print from_json("[\"fe80::/64\",null,\"192.168.0.0/16\"]", subnet_set);
print from_json("[\"1\",null,\"3\",\"4\"]", string_vec);
}
@TEST-START-NEXT
type Foo: record {
hello: string;
t: bool;
};
# record field null or missing
event zeek_init()
{
print from_json("{\"t\":null}", Foo);
print from_json("{\"hello\": null, \"t\": true}", Foo);
}
@TEST-START-NEXT
type Foo: record {
hello: string;
};
# extra fields are alright
event zeek_init()
{
print from_json("{\"hello\": \"Hello!\", \"t\": true}", Foo);
}
@TEST-START-NEXT
type Foo: record {
id_field: string;
};
# test key_fun for n
event zeek_init()
{
print from_json("{\"id-field\": \"Hello!\"}", Foo);
print from_json("{\"id-field\": \"Hello!\"}", Foo, function(s: string): string {
return gsub(s, /[^a-zA-Z0-9_]/, "_");
});
}
@TEST-START-NEXT
# From: https://www.rfc-editor.org/rfc/rfc8520#section-9
global input:string = "{\"ietf-mud:mud\":{\"mud-version\":1,\"mud-url\":\"https://lighting.example.com/lightbulb2000\",\"last-update\":\"2019-01-28T11:20:51+01:00\",\"cache-validity\":48,\"is-supported\":true,\"systeminfo\":\"The BMS Example Light Bulb\",\"from-device-policy\":{\"access-lists\":{\"access-list\":[{\"name\":\"mud-76100-v6fr\"}]}},\"to-device-policy\":{\"access-lists\":{\"access-list\":[{\"name\":\"mud-76100-v6to\"}]}}},\"ietf-access-control-list:acls\":{\"acl\":[{\"name\":\"mud-76100-v6to\",\"type\":\"ipv6-acl-type\",\"aces\":{\"ace\":[{\"name\":\"cl0-todev\",\"matches\":{\"ipv6\":{\"ietf-acldns:src-dnsname\":\"test.example.com\",\"protocol\":6},\"tcp\":{\"ietf-mud:direction-initiated\":\"from-device\",\"source-port\":{\"operator\":\"eq\",\"port\":443}}},\"actions\":{\"forwarding\":\"accept\"}}]}},{\"name\":\"mud-76100-v6fr\",\"type\":\"ipv6-acl-type\",\"aces\":{\"ace\":[{\"name\":\"cl0-frdev\",\"matches\":{\"ipv6\":{\"ietf-acldns:dst-dnsname\":\"test.example.com\",\"protocol\":6},\"tcp\":{\"ietf-mud:direction-initiated\":\"from-device\",\"destination-port\":{\"operator\":\"eq\",\"port\":443}}},\"actions\":{\"forwarding\":\"accept\"}}]}}]}}";
module Mud8520;
type MudAccessList: record {
name: string;
};
type MudAccessLists: record {
access_list: vector of MudAccessList;
};
type MudPolicy: record {
access_lists: MudAccessLists;
};
type MudMatchPort: record {
operator: string;
_port: count;
};
type MudMatch: record {
ietf_acldns_dst_dnsname: string &optional;
ietf_mud_direction_initiated: string &optional;
destination_port: MudMatchPort &optional;
};
type MudMatches: record {
# Might make more sense to have individual types.
ipv6: MudMatch &optional;
tcp: MudMatch &optional;
};
type MudAce: record {
name: string;
matches: MudMatches;
};
type MudAces: record {
ace: vector of MudAce;
};
type MudAcl: record {
name: string;
_type: string;
aces: MudAces;
};
type MudAcls: record {
acl: vector of MudAcl;
};
type Mud: record {
mud_version: count;
mud_url: string;
last_update: string;
cache_validity: count;
is_supported: bool;
systeminfo: string;
from_device_policy: MudPolicy;
to_device_policy: MudPolicy;
};
type MudDocument: record {
ietf_mud_mud: Mud;
ietf_access_control_list_acls: MudAcls;
};
event zeek_init()
{
local mud_mapping_table: table[string] of string = {
["type"] = "_type",
["port"] = "_port",
};
local mud_json_key_fun = function [mud_mapping_table](s: string): string {
if ( s in mud_mapping_table )
return mud_mapping_table[s];
return gsub(s, /[^a-zA-Z0-9_]/, "_");
};
local result = from_json(input, MudDocument, mud_json_key_fun);
print result$valid;
print result$v;
}