mirror of
https://github.com/rsyslog/rsyslog.git
synced 2026-04-23 16:08:12 +02:00
rainerscript: ensure parse_json consumes entire input string
This fix ensures that parse_json() only succeeds if the entire input string is a valid JSON value. This prevents false positives when a non-JSON string happens to start with a valid JSON value, like a number. Documentation is updated to reflect this stricter validation. Impact: Corrects false-success in parse_json() for malformed input. Modified doFunc_parse_json in grammar/rainerscript.c to check if the json-c tokener consumed the entire provided string. After parsing, the remainder of the string is scanned for any non-whitespace characters. If trailing garbage is found, the function now returns RS_SCRIPT_EINVAL instead of RS_SCRIPT_EOK. Updated rs-parse_json.rst to document the requirement for a complete JSON object/value. Added a regression test and updated the testbench Makefile.am to include the new validation scenario. Fixes: https://github.com/rsyslog/rsyslog/issues/4970 AI-Agent: Antigravity
This commit is contained in:
parent
8e1fa59ae1
commit
1ab067b680
@ -9,8 +9,11 @@ parse_json(str, container)
|
||||
|
||||
Parses the json string ``str`` and places the resulting json object
|
||||
into ``container`` where container can be any valid rsyslog variable.
|
||||
Note that the **entire** ``str`` must be valid JSON for the function
|
||||
to succeed. If there is trailing data after a valid JSON object/value,
|
||||
it will be considered as an error.
|
||||
Returns 0 on success and something otherwise if ``str`` does **not**
|
||||
contain valid json.
|
||||
contain a valid, complete json string.
|
||||
|
||||
|
||||
Example
|
||||
|
||||
@ -1822,11 +1822,22 @@ static void ATTR_NONNULL() doFunc_parse_json(struct cnffunc *__restrict__ const
|
||||
json = json_tokener_parse_ex(tokener, jsontext, strlen(jsontext));
|
||||
if (json == NULL) {
|
||||
retVal = RS_SCRIPT_EINVAL;
|
||||
} else {
|
||||
/* Check for trailing garbage */
|
||||
int i = tokener->char_offset;
|
||||
while (jsontext[i] != '\0' && isspace((uchar)jsontext[i])) {
|
||||
i++;
|
||||
}
|
||||
if (jsontext[i] != '\0') {
|
||||
json_object_put(json);
|
||||
json = NULL;
|
||||
retVal = RS_SCRIPT_EINVAL;
|
||||
} else {
|
||||
size_t off = (*container == '$') ? 1 : 0;
|
||||
msgAddJSON(pMsg, (uchar *)container + off, json, 0, 0);
|
||||
retVal = RS_SCRIPT_EOK;
|
||||
}
|
||||
}
|
||||
wtiSetScriptErrno(pWti, retVal);
|
||||
json_tokener_free(tokener);
|
||||
|
||||
|
||||
@ -1321,7 +1321,8 @@ static rsRetVal findIPv6(struct ipv6_int *num, char *address, wrkrInstanceData_t
|
||||
struct hashtable *randConsisUniqueGeneratedIPs = useEmbedded
|
||||
? pWrkrData->pData->embeddedIPv4.randConsisUniqueGeneratedIPs
|
||||
: pWrkrData->pData->ipv6.randConsisUniqueGeneratedIPs;
|
||||
const int uniqueMode = useEmbedded ? pWrkrData->pData->embeddedIPv4.randConsisUnique : pWrkrData->pData->ipv6.randConsisUnique;
|
||||
const int uniqueMode =
|
||||
useEmbedded ? pWrkrData->pData->embeddedIPv4.randConsisUnique : pWrkrData->pData->ipv6.randConsisUnique;
|
||||
struct ipv6_int original = *num;
|
||||
struct ipv6_int *uniqueKey = NULL;
|
||||
sbool locked = 0;
|
||||
|
||||
@ -449,6 +449,7 @@ TESTS += \
|
||||
rscript_is_time.sh \
|
||||
rscript_script_error.sh \
|
||||
rscript_parse_json.sh \
|
||||
rscript_parse_json_issue.sh \
|
||||
rscript_previous_action_suspended.sh \
|
||||
rscript_str2num_negative.sh \
|
||||
rscript_exists-yes.sh \
|
||||
@ -2286,6 +2287,7 @@ EXTRA_DIST= \
|
||||
rscript_is_time.sh \
|
||||
rscript_script_error.sh \
|
||||
rscript_parse_json.sh \
|
||||
rscript_parse_json_issue.sh \
|
||||
rscript_parse_json-vg.sh \
|
||||
rscript_backticks-vg.sh \
|
||||
rscript_backticks_empty_envvar.sh \
|
||||
|
||||
25
tests/rscript_parse_json_issue.sh
Executable file
25
tests/rscript_parse_json_issue.sh
Executable file
@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
# Reproduction for parse_json() issue
|
||||
. ${srcdir:=.}/diag.sh init
|
||||
generate_conf
|
||||
add_conf '
|
||||
module(load="../plugins/imtcp/.libs/imtcp")
|
||||
input(type="imtcp" port="0" listenPortFileName="'$RSYSLOG_DYNNAME'.tcpflood_port")
|
||||
template(name="outfmt" type="string" string="ret: %$.ret%, parsed: %$!parsed%\n")
|
||||
|
||||
local4.* {
|
||||
set $.ret = parse_json("22 08 23 this is a test message", "\$!parsed");
|
||||
action(type="omfile" file=`echo $RSYSLOG_OUT_LOG` template="outfmt")
|
||||
}
|
||||
'
|
||||
startup
|
||||
tcpflood -m1
|
||||
shutdown_when_empty
|
||||
wait_shutdown
|
||||
|
||||
export EXPECTED='ret: 1, parsed: '
|
||||
# Since the bug is that it returns 0 and parses "22", we expect ret: 0 and parsed: 22 if it fails.
|
||||
# We want it to be 1 and empty.
|
||||
cmp_exact $RSYSLOG_OUT_LOG
|
||||
|
||||
exit_test
|
||||
Loading…
x
Reference in New Issue
Block a user