Skip to content

Harden CSDL attribute round trips - #26

Merged
nikstern merged 2 commits into
mainfrom
codex/issue-23-csdl-escaping
Aug 23, 2026
Merged

Harden CSDL attribute round trips#26
nikstern merged 2 commits into
mainfrom
codex/issue-23-csdl-escaping

Conversation

@nikstern

@nikstern nikstern commented Aug 23, 2026

Copy link
Copy Markdown
Owner

Summary

Implements #23 by porting the CSDL hardening behavior from nerdsane#409 onto this fork.

  • routes every emitted string attribute through centralized XML escaping
  • decodes named, decimal, and hexadecimal entity references exactly once
  • uses a separate text-node escape/decode path so collection whitespace and markup characters round-trip
  • covers hostile payloads across properties, streams, annotations, operations, bindings, references, typed defaults, and generated SDK inputs

Deliberate semantic difference from upstream nerdsane#409

Malformed entity syntax now fails parsing, including in optional attributes, instead of silently turning a present value into an absent value. This is stricter and may reject previously tolerated malformed CSDL, but it prevents invalid typed metadata from silently falling back to defaults.

Validation

  • cargo test -p temper-spec — 284 unit tests plus integration and fixture suites passed
  • cargo clippy -p temper-spec --all-targets -- -D warnings
  • cargo test -p temper-codegen --test generated_sdk_compile
  • cargo test -p temper-verify
  • cargo test --workspace — passed, including HTTP metadata E2E and verification cascade coverage
  • mandatory code-quality re-review — PASS, no findings

Closes #23

nerdsane and others added 2 commits August 23, 2026 14:11
`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>
@nikstern
nikstern marked this pull request as ready for review August 23, 2026 21:52
@nikstern
nikstern merged commit e4ff280 into main Aug 23, 2026
25 checks passed
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.

Port CSDL attribute escaping and entity-reference decoding

2 participants