mirror of
https://github.com/aljazceru/lightning.git
synced 2025-12-19 15:14:23 +01:00
json: make json_add_string do partial escapes.
This is useful when we log a JSON-escaped string, so we don't double-escape. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
@@ -4,6 +4,7 @@ LIGHTNING_CLI_OBJS := $(LIGHTNING_CLI_SRC:.c=.o)
|
|||||||
LIGHTNING_CLI_COMMON_OBJS := \
|
LIGHTNING_CLI_COMMON_OBJS := \
|
||||||
common/configdir.o \
|
common/configdir.o \
|
||||||
common/json.o \
|
common/json.o \
|
||||||
|
common/json_escaped.o \
|
||||||
common/version.o
|
common/version.o
|
||||||
|
|
||||||
lightning-cli-all: cli/lightning-cli
|
lightning-cli-all: cli/lightning-cli
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ CLI_TEST_COMMON_OBJS := \
|
|||||||
common/daemon_conn.o \
|
common/daemon_conn.o \
|
||||||
common/htlc_state.o \
|
common/htlc_state.o \
|
||||||
common/json.o \
|
common/json.o \
|
||||||
|
common/json_escaped.o \
|
||||||
common/pseudorand.o \
|
common/pseudorand.o \
|
||||||
common/memleak.o \
|
common/memleak.o \
|
||||||
common/msg_queue.o \
|
common/msg_queue.o \
|
||||||
|
|||||||
@@ -409,18 +409,11 @@ void json_add_literal(struct json_result *result, const char *fieldname,
|
|||||||
|
|
||||||
void json_add_string(struct json_result *result, const char *fieldname, const char *value)
|
void json_add_string(struct json_result *result, const char *fieldname, const char *value)
|
||||||
{
|
{
|
||||||
char *escaped = tal_strdup(result, value);
|
struct json_escaped *esc = json_partial_escape(NULL, value);
|
||||||
size_t i;
|
|
||||||
|
|
||||||
json_start_member(result, fieldname);
|
json_start_member(result, fieldname);
|
||||||
for (i = 0; escaped[i]; i++) {
|
result_append_fmt(result, "\"%s\"", esc->s);
|
||||||
/* Replace any funny business. Better safe than accurate! */
|
tal_free(esc);
|
||||||
if (escaped[i] == '\\'
|
|
||||||
|| escaped[i] == '"'
|
|
||||||
|| !cisprint(escaped[i]))
|
|
||||||
escaped[i] = '?';
|
|
||||||
}
|
|
||||||
result_append_fmt(result, "\"%s\"", escaped);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void json_add_bool(struct json_result *result, const char *fieldname, bool value)
|
void json_add_bool(struct json_result *result, const char *fieldname, bool value)
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ void json_object_end(struct json_result *ptr);
|
|||||||
struct json_result *new_json_result(const tal_t *ctx);
|
struct json_result *new_json_result(const tal_t *ctx);
|
||||||
|
|
||||||
/* '"fieldname" : "value"' or '"value"' if fieldname is NULL. Turns
|
/* '"fieldname" : "value"' or '"value"' if fieldname is NULL. Turns
|
||||||
* any unusual chars into ?.
|
* any non-printable chars into JSON escapes, but leaves existing escapes alone.
|
||||||
*/
|
*/
|
||||||
void json_add_string(struct json_result *result, const char *fieldname, const char *value);
|
void json_add_string(struct json_result *result, const char *fieldname, const char *value);
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,9 @@ bool json_escaped_eq(const struct json_escaped *a,
|
|||||||
return streq(a->s, b->s);
|
return streq(a->s, b->s);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct json_escaped *json_escape(const tal_t *ctx, const char *str TAKES)
|
static struct json_escaped *escape(const tal_t *ctx,
|
||||||
|
const char *str TAKES,
|
||||||
|
bool partial)
|
||||||
{
|
{
|
||||||
struct json_escaped *esc;
|
struct json_escaped *esc;
|
||||||
size_t i, n;
|
size_t i, n;
|
||||||
@@ -62,6 +64,31 @@ struct json_escaped *json_escape(const tal_t *ctx, const char *str TAKES)
|
|||||||
escape = 'r';
|
escape = 'r';
|
||||||
break;
|
break;
|
||||||
case '\\':
|
case '\\':
|
||||||
|
if (partial) {
|
||||||
|
/* Don't double-escape standard escapes. */
|
||||||
|
if (str[i+1] == 'n'
|
||||||
|
|| str[i+1] == 'b'
|
||||||
|
|| str[i+1] == 'f'
|
||||||
|
|| str[i+1] == 't'
|
||||||
|
|| str[i+1] == 'r'
|
||||||
|
|| str[i+1] == '/'
|
||||||
|
|| str[i+1] == '\\'
|
||||||
|
|| str[i+1] == '"') {
|
||||||
|
escape = str[i+1];
|
||||||
|
i++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (str[i+1] == 'u'
|
||||||
|
&& cisxdigit(str[i+2])
|
||||||
|
&& cisxdigit(str[i+3])
|
||||||
|
&& cisxdigit(str[i+4])
|
||||||
|
&& cisxdigit(str[i+5])) {
|
||||||
|
memcpy(esc->s + n, str + i, 6);
|
||||||
|
n += 5;
|
||||||
|
i += 5;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} /* fall thru */
|
||||||
case '"':
|
case '"':
|
||||||
escape = str[i];
|
escape = str[i];
|
||||||
break;
|
break;
|
||||||
@@ -85,6 +112,16 @@ struct json_escaped *json_escape(const tal_t *ctx, const char *str TAKES)
|
|||||||
return esc;
|
return esc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct json_escaped *json_partial_escape(const tal_t *ctx, const char *str TAKES)
|
||||||
|
{
|
||||||
|
return escape(ctx, str, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct json_escaped *json_escape(const tal_t *ctx, const char *str TAKES)
|
||||||
|
{
|
||||||
|
return escape(ctx, str, false);
|
||||||
|
}
|
||||||
|
|
||||||
/* By policy, we don't handle \u. Use UTF-8. */
|
/* By policy, we don't handle \u. Use UTF-8. */
|
||||||
const char *json_escaped_unescape(const tal_t *ctx,
|
const char *json_escaped_unescape(const tal_t *ctx,
|
||||||
const struct json_escaped *esc)
|
const struct json_escaped *esc)
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ struct json_escaped {
|
|||||||
/* @str be a valid UTF-8 string */
|
/* @str be a valid UTF-8 string */
|
||||||
struct json_escaped *json_escape(const tal_t *ctx, const char *str TAKES);
|
struct json_escaped *json_escape(const tal_t *ctx, const char *str TAKES);
|
||||||
|
|
||||||
|
/* @str is a valid UTF-8 string which may already contain escapes. */
|
||||||
|
struct json_escaped *json_partial_escape(const tal_t *ctx,
|
||||||
|
const char *str TAKES);
|
||||||
|
|
||||||
/* Extract a JSON-escaped string. */
|
/* Extract a JSON-escaped string. */
|
||||||
struct json_escaped *json_tok_escaped_string(const tal_t *ctx,
|
struct json_escaped *json_tok_escaped_string(const tal_t *ctx,
|
||||||
const char *buffer,
|
const char *buffer,
|
||||||
|
|||||||
@@ -67,12 +67,13 @@ static int test_json_filter(void)
|
|||||||
x = json_get_member(str, toks, "x");
|
x = json_get_member(str, toks, "x");
|
||||||
assert(x);
|
assert(x);
|
||||||
assert(x->type == JSMN_STRING);
|
assert(x->type == JSMN_STRING);
|
||||||
assert((x->end - x->start) == 255);
|
|
||||||
|
/* There are 7 one-letter escapes, and (32-5) \uXXXX escapes. */
|
||||||
|
assert((x->end - x->start) == 255 + 7*1 + (32-5)*5);
|
||||||
|
/* No control characters. */
|
||||||
for (i = x->start; i < x->end; i++) {
|
for (i = x->start; i < x->end; i++) {
|
||||||
assert(cisprint(str[i]));
|
assert((unsigned)str[i] >= ' ');
|
||||||
assert(str[i] != '\\');
|
assert((unsigned)str[i] != 127);
|
||||||
assert(str[i] != '"');
|
|
||||||
assert(str[i] == '?' || str[i] == badstr[i - x->start]);
|
|
||||||
}
|
}
|
||||||
tal_free(result);
|
tal_free(result);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -112,11 +113,44 @@ static void test_json_escape(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_json_partial(void)
|
||||||
|
{
|
||||||
|
const tal_t *ctx = tal(NULL, char);
|
||||||
|
|
||||||
|
assert(streq(json_partial_escape(ctx, "\\")->s, "\\\\"));
|
||||||
|
assert(streq(json_partial_escape(ctx, "\\\\")->s, "\\\\"));
|
||||||
|
assert(streq(json_partial_escape(ctx, "\\\\\\")->s, "\\\\\\\\"));
|
||||||
|
assert(streq(json_partial_escape(ctx, "\\\\\\\\")->s, "\\\\\\\\"));
|
||||||
|
assert(streq(json_partial_escape(ctx, "\\n")->s, "\\n"));
|
||||||
|
assert(streq(json_partial_escape(ctx, "\n")->s, "\\n"));
|
||||||
|
assert(streq(json_partial_escape(ctx, "\\\"")->s, "\\\""));
|
||||||
|
assert(streq(json_partial_escape(ctx, "\"")->s, "\\\""));
|
||||||
|
assert(streq(json_partial_escape(ctx, "\\t")->s, "\\t"));
|
||||||
|
assert(streq(json_partial_escape(ctx, "\t")->s, "\\t"));
|
||||||
|
assert(streq(json_partial_escape(ctx, "\\b")->s, "\\b"));
|
||||||
|
assert(streq(json_partial_escape(ctx, "\b")->s, "\\b"));
|
||||||
|
assert(streq(json_partial_escape(ctx, "\\r")->s, "\\r"));
|
||||||
|
assert(streq(json_partial_escape(ctx, "\r")->s, "\\r"));
|
||||||
|
assert(streq(json_partial_escape(ctx, "\\f")->s, "\\f"));
|
||||||
|
assert(streq(json_partial_escape(ctx, "\f")->s, "\\f"));
|
||||||
|
/* You're allowed to escape / according to json.org. */
|
||||||
|
assert(streq(json_partial_escape(ctx, "\\/")->s, "\\/"));
|
||||||
|
assert(streq(json_partial_escape(ctx, "/")->s, "/"));
|
||||||
|
|
||||||
|
assert(streq(json_partial_escape(ctx, "\\u0FFF")->s, "\\u0FFF"));
|
||||||
|
assert(streq(json_partial_escape(ctx, "\\u0FFFx")->s, "\\u0FFFx"));
|
||||||
|
|
||||||
|
/* Unknown escapes should be escaped. */
|
||||||
|
assert(streq(json_partial_escape(ctx, "\\x")->s, "\\\\x"));
|
||||||
|
tal_free(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
test_json_tok_bitcoin_amount();
|
test_json_tok_bitcoin_amount();
|
||||||
test_json_filter();
|
test_json_filter();
|
||||||
test_json_escape();
|
test_json_escape();
|
||||||
|
test_json_partial();
|
||||||
assert(!taken_any());
|
assert(!taken_any());
|
||||||
take_cleanup();
|
take_cleanup();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user