Skip to content

Omit parsed JSON values from JsonParseError messages - #8363

Open
Amaury Chamayou (achamayou) wants to merge 2 commits into
mainfrom
achamayou-json-parse-error-no-dump
Open

Omit parsed JSON values from JsonParseError messages#8363
Amaury Chamayou (achamayou) wants to merge 2 commits into
mainfrom
achamayou-json-parse-error-no-dump

Conversation

@achamayou

Copy link
Copy Markdown
Member

Follow-up identified during the final security review of #8354.

Problem

The READ_REQUIRED_* macros in include/ccf/ds/json.h built the missing-required-field error as "Missing required field '<name>' in object: " + j.dump(), and the from_json_required_fields type check used "Expected object, found: " + j.dump(). For a private JWK passed to ccf.crypto.jwkToPem() with a missing field (e.g. qi), the exception what() therefore contained d, p, q, dp, dq in full. That text becomes the JS InternalError message and, when the debug-only log_exception_details / return_exception_details options are enabled, reaches node logs or HTTP responses. The exception storage and dump() temporaries are also outside the reach of the scrubbing guards added in #8354.

Changes

  • include/ccf/ds/json.h: add ccf::describe_json_keys() and use it in the missing-required-field message, so the error lists the field names present but no values. The non-object message now reports j.type_name() instead of the serialised value.
  • src/ds/test/json_schema.cpp: regression tests asserting that values are absent from both messages, for the plain and _WITH_RENAMES macro paths.
  • CHANGELOG.md: entry under 7.0.16 Fixed.

No JS-visible result or error type changes; only the exception message text differs.

Validation

  • json_schema unit test compiled directly with clang (header-only target) and run: 15 test cases, 164 assertions pass.
  • clang-format --dry-run --Werror on both C++ files: clean. Prettier on CHANGELOG.md: clean.
  • scripts/ci-checks.sh could not be run locally (Windows CRLF checkout breaks the bash scripts and the worktree .git pointer under WSL); CI is required before merge.

Missing-required-field and non-object errors serialised the full input
into the exception message, which for private JWKs included key
material. Report the present field names or JSON type name instead.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings September 12, 2026 12:25
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Unresolved findings remain around key escaping, renamed type-check coverage, test assertions, and changelog scope.

Pull request overview

Updates JSON parse errors to omit parsed values, reducing sensitive data exposure.

Changes:

  • Reports JSON keys or type names instead of serialized values.
  • Adds regression tests for standard and renamed macros.
  • Documents the fix in the changelog.
File summaries
File Changes
src/ds/test/json_schema.cpp Adds regression coverage for sanitized errors.
include/ccf/ds/json.h Removes parsed values from JSON diagnostics.
CHANGELOG.md Documents the security fix.
Review details

Suppressed comments (4)

CHANGELOG.md:21

  • This release note is broader than the implementation: src/node/receipt.cpp still puts j.dump() into JsonParseError messages for missing required receipt fields and non-object receipt values. Either update those remaining paths or qualify this entry as applying to the JSON-schema macro-generated messages changed here.
- `ccf::JsonParseError` messages for a missing required field or a non-object value no longer include a serialisation of the parsed JSON, which could contain sensitive values such as private JWK fields. They now list the field names present, or the JSON type found (#8363).

include/ccf/ds/json.h:55

  • Object member names are untrusted JSON input, but this appends them verbatim. A key containing a newline or another control character will therefore enter JsonParseError::what() and be logged as raw text when log_exception_details is enabled, allowing log-line injection; the old j.dump() escaped these characters. Escape/JSON-quote each key before joining while still omitting values.
      keys.push_back(item.key());

include/ccf/ds/json.h:855

  • The _WITH_RENAMES macro has a separate non-object type-check branch here, but the new tests only exercise the renamed missing-field branch. A regression in this branch could reintroduce j.dump() for scalar input while the suite still passes; add a renamed-type scalar-input assertion that reports the JSON type and excludes SECRET_VALUE.
      throw ccf::JsonParseError( \
        std::string("Expected object, found: ") + j.type_name()); \

src/ds/test/json_schema.cpp:62

  • These substring checks do not verify that the helper actually lists b and c: both letters occur in the surrounding word object, so an empty field list would still pass. Assert the emitted list (for example, fields: [b, c]) so this regression test covers the new behavior.
      REQUIRE(msg.find("b") != std::string::npos);
      REQUIRE(msg.find("c") != std::string::npos);
  • Files reviewed: 3/3 changed files
  • Comments generated: 0
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants