Skip to content

fix(csdl): escape attributes and reject malformed entity references - #409

Open
NALLSUR wants to merge 8 commits into
mainfrom
surendranalla/arn-237-bug-csdl-parser-accepts-truncated-or-defaulted-schemas-and
Open

fix(csdl): escape attributes and reject malformed entity references#409
NALLSUR wants to merge 8 commits into
mainfrom
surendranalla/arn-237-bug-csdl-parser-accepts-truncated-or-defaulted-schemas-and

Conversation

@NALLSUR

@NALLSUR NALLSUR commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

Merge preparation is in progress. Corrections are committed locally at dc506e82780ae4000b50389707e6de22dbf42d93 and have not been pushed because required workspace checks have not passed. No merge or deployment is claimed.

Escape CSDL attribute values so quotes and XML metacharacters cannot inject markup. Decode valid entity references on parse, preserve attribute whitespace through character references, and return XML decode errors instead of silently discarding malformed optional attributes. Collection text keeps its existing whitespace behavior.

The change is limited to CSDL serialization, attribute decoding, regression tests, and effort documents. Truncated-document rejection, broader schema strictness, and registry recovery remain outside this PR under ARN-237.

Validation

The final source passed 288 temper-spec unit tests, three migration tests, targeted clippy, and formatting. A live local server returned HTTP 200 metadata whose parsed attribute value preserved quotes, ampersands, angle brackets, tabs, LF, and CR. The three-model local panel completed on dc506e82 with no blocking findings. Full workspace push checks and current-head Greptile verification remain pending; no merge or production deployment is claimed.

Malformed stored attributes now fail parsing instead of being silently discarded. This accepted decoder behavior is a deployment compatibility risk; affected stored schemas must be assessed before rollout. The nonblocking Collection comment nit is acknowledged; collection text keeps its existing decoding behavior.

Decisions & Tradeoffs

Decision

Narrow the parser fix to entity decoding failures.

Came up because PR #409 changes raw values to unescape_value().ok(), which silently defaults a present malformed optional attribute.

Options: defer that new defaulting behavior; rewrite all strict parsing; propagate the decoding error alone.

Chose targeted error propagation over broad strict parsing because it resolves the new behavior while preserving the separately planned truncated-schema/startup recovery work.

Where: PR #409, crates/temper-spec/src/csdl/parser/xml.rs.

Merge reconciliation

Decision: retain current main for files outside the CSDL escaping change.

Came up because dependency-sync commits added while this task was paused conflicted with current main across kernel and infrastructure files.

Options: retain stale copied files; independently rework unrelated systems; take current main and preserve the PR's emitter, round-trip tests, parser decoding, and effort documents.

Chose current main for unrelated files because the sync commits intended dependency compatibility, not replacement of newer kernel behavior. This keeps the final change scoped to CSDL escaping and explicit decoding errors, using quick-xml 0.41's normalized_value API.

Where: PR #409, merge of origin/main aa22bf1 into the existing branch.

Collection text whitespace

Decision: keep XML text escaping separate from attribute whitespace escaping.

Came up because review reproduced an embedded collection newline changing into literal character-reference text after emit/parse.

Options: broaden collection parsing to decode all text entities; retain the regression; use existing quick-xml text escaping for collection text.

Chose text escaping because it preserves the prior collection behavior while attribute normalization remains corrected. Broader collection decoding is pre-existing work.

Where: PR #409, emit.rs collection emission and emit_test.rs whitespace regression.

Original contribution retained; merge preparation and corrections by GPT-6 in Codex. Required review: Grok 4.6, Codex gpt-5.6-sol, Fable, and Greptile. Current-head review/proof evidence is being completed.

nerdsane and others added 2 commits July 22, 2026 00:22
`attr_str` returned `Attribute::value` verbatim, which is the raw,
still-escaped byte range. Any CSDL containing `&`, `"` or a
numeric character reference in an attribute therefore parsed into the
typed model as the literal escape text rather than the character it
denotes.

This is load-bearing for the emitter escaping that follows: with the
emitter escaping correctly and the parser not unescaping, every
parse/emit/parse cycle would add a layer (`&` -> `&` -> `&`).
The two changes are only correct together.

No existing behaviour changes for the current corpus — no CSDL fixture
in the repository contains an entity reference — so this commit is a
no-op for the suite and is separated purely to keep the shared read-path
change reviewable on its own.

Refs ARN-237.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`emit_csdl_xml` applied `xml_escape` to only 4 of roughly 30
interpolation sites. Names, types, namespaces, DefaultValue,
navigation-binding Path/Target, action/function references and
annotation terms were all written into attributes verbatim. Those values
are agent- and user-influenced, so a `"` in an identifier closed the
attribute and let the remainder of the value inject arbitrary markup —
the same defect class as ARN-172. A property named

    Name"/><Property Name="Smuggled

produced a second, unintended property element, and a smuggled
`HasStream="true"` altered the typed model on re-parse.

Every interpolated string value now passes through `xml_escape`. The
remaining unescaped interpolations are numeric (`i64`, `u32`, `f64`,
`bool`) and cannot produce a metacharacter.

`xml_escape` additionally escapes tab, newline and carriage return as
character references. XML attribute-value normalisation replaces literal
occurrences with spaces, so without this a multi-line DefaultValue or
Description came back altered even when the quoting was correct. The
function is also now a single pass rather than five chained `replace`
calls.

Tests cover both properties: `adversarial_identifiers_do_not_inject_markup`
asserts that no live markup is emitted and that the typed model survives
a round trip unchanged, across entity types, keys, properties,
annotations, containers, entity sets, navigation bindings and
action/function imports; `whitespace_in_attribute_values_round_trips`
pins the normalisation behaviour.

This is the emitter half of ARN-237. The strict-parsing half — rejecting
truncated schemas, mandatory attributes, unknown-element rejection — is
deliberately deferred: it increases the set of persisted schemas that
fail to load, which interacts directly with ARN-190, where one corrupt
tenant CSDL aborts the entire registry restore.

Refs ARN-237.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@NALLSUR
NALLSUR force-pushed the surendranalla/arn-237-bug-csdl-parser-accepts-truncated-or-defaulted-schemas-and branch from 9e40ee1 to a5e8053 Compare July 22, 2026 21:57
@NALLSUR
NALLSUR marked this pull request as ready for review July 23, 2026 19:18
.flatten()
.find(|attribute| std::str::from_utf8(attribute.key.as_ref()).unwrap_or("") == name)
.and_then(|attribute| String::from_utf8(attribute.value.to_vec()).ok())
.and_then(|attribute| attribute.unescape_value().ok())

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Malformed entities erase optional attributes

When an optional attribute contains a malformed entity reference, converting unescape_value() errors to None makes the parser treat the present attribute as absent, causing annotations to be dropped or values such as nullability, defaults, paths, and targets to silently revert to fallback values.

Context Used: CLAUDE.md (source)

Knowledge Base Used: Temper spec format, codegen, and verification

Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/temper-spec/src/csdl/parser/xml.rs
Line: 51

Comment:
**Malformed entities erase optional attributes**

When an optional attribute contains a malformed entity reference, converting `unescape_value()` errors to `None` makes the parser treat the present attribute as absent, causing annotations to be dropped or values such as nullability, defaults, paths, and targets to silently revert to fallback values.

**Context Used:** CLAUDE.md ([source](https://github.com/nerdsane/temper/blob/main/CLAUDE.md))

**Knowledge Base Used:** [Temper spec format, codegen, and verification](https://app.greptile.com/arni-labs/-/custom-context/knowledge-base/nerdsane/temper/-/docs/temper-spec.md)

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code Fix in Codex Fix in Cursor

@greptile-apps

greptile-apps Bot commented Sep 9, 2026

Copy link
Copy Markdown

Too many files changed for review (348 files, 100 file limit).

Bypass the limit by tagging @greptile-apps to review.

@rita-aga rita-aga changed the title Escape all CSDL attribute values on emit; decode entity references on parse (ARN-237) fix(csdl): escape attributes and reject malformed entity references Sep 9, 2026
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.

3 participants