Repository navigation
fix(agent): refuse invisible format characters in metadata - #2166
Open
Benoît Cortier (CBenoit) wants to merge 9 commits into
Open
Benoît Cortier (CBenoit) wants to merge 9 commits into
Benoît Cortier (CBenoit) wants to merge 9 commits into
Conversation
A metadata value can hold bidirectional overrides and zero-width characters that reorder or hide text where an administrator reads it, such as the friendly name an authority evaluates from the host name. Metadata values now exclude a closed list of invisible format characters, and the agent removes them before sending. The list is fixed as code points rather than a Unicode general category, so that the agent and every authority agree on each code point whatever Unicode version their runtimes ship. Issue: DGW-478 Issue: DGW-479 Security: yes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An authority refuses each end of every listed range at enrollment without consuming a token use, and accepts and stores the code points just outside the ranges and unlisted format characters. The agent removes listed code points from the metadata it sends and sends `unknown` for a required key left empty. The friendly-name trim case moves to a format character that the list leaves out, since U+200B is now refused. Issue: DGW-478 Security: yes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each refused enrollment now differs from the accepted control only by the listed code point, and an unknown key holding one is refused too. The friendly-name trim also covers a format character in the Basic Multilingual Plane, the agent check covers an optional key left empty, and harness tests pin the code point lists and the agent metadata check. Issue: DGW-478 Security: yes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The mock authority now answers `invalid_request` to a metadata value holding any invisible format character that the contract lists, so that no value can reorder or hide text where an administrator reads it, such as a friendly name. The list is matched as fixed code points rather than as Unicode category `Cf`, so the mock agrees with every party whatever Unicode version it ships. Issue: DGW-478 Security: yes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The agent now removes from each metadata value the invisible format characters that the Agent Identity contract forbids, such as bidirectional overrides, zero-width characters and tag characters, along with the control characters it already removed. It therefore never sends a value an authority refuses, and no reported value can reorder or hide text where an administrator reads it, such as in a friendly name evaluated from metadata. The characters are matched as the contract's fixed list of code points rather than a Unicode category, so the agent and every authority agree whatever Unicode version their runtimes ship. Issue: DGW-478 Security: yes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…adata `AgentMetadata.Validate` now refuses a value that holds one of the invisible format characters the contract lists: U+00AD, U+061C, U+180E, U+200B to U+200F, U+202A to U+202E, U+2060 to U+2064, U+2066 to U+206F, U+FEFF, U+FFF9 to U+FFFB, U+E0001 and U+E0020 to U+E007F. Such a character can reorder or hide text where an administrator reads it, as in a friendly name evaluated from metadata. Enroll, renew and check-in bodies and the `Hello` of a channel stream therefore answer such a value with `invalid_request`. The list is a fixed set of code points rather than the runtime's Format category, so the agent and every authority agree whatever Unicode version their runtimes ship, and the visible format marks such as U+0600 stay accepted. The failure detail names neither the character nor its code point, since the value comes from the request. Issue: DGW-479 Security: yes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Issue: DGW-478 Issue: DGW-479 Security: yes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The library now refuses metadata it accepted before, so authorities need a new package to pick up the rule. Issue: DGW-479 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Member
Author
Evidence at
|
| Check | Result |
|---|---|
cargo +nightly fmt --all -- --check |
clean |
cargo clippy -p agent-identity -p agent-identity-mock -p agent-identity-conformance --all-targets -- -D warnings |
clean |
cargo test -p agent-identity -p agent-identity-mock -p agent-identity-conformance |
185, 509 + 10 and 51 passed (1 ignored) |
build.ps1 (authority library) |
0 warnings |
dotnet test (authority library) |
2111 passed |
ci/check-oracle-markers.ps1 -Base origin/master |
exit 0 |
| Conformance, mock, file key backend (Windows) | 190 PASS, 0 FAIL, 1 SKIP (a_key_non_exportable needs the Windows provider), 2 N/A |
Conformance through testsuite, Windows, not elevated |
136 PASS, 0 FAIL; agent tests SKIP without elevation, covered by the run above |
The new conformance checks (p_metadata_invisible_format_characters, the U+E007F Hello case, a_metadata_invisible_format_characters_removed) failed against the mock and agent before their changes and pass after.
The suite changes were written and reviewed from the contract only, without reading the implementations.
Notes for the reviewer
- The package version is
2026.10.28, after fix(dotnet-agent-identity)!: harden issuance and nonce retention #2164's2026.10.27; this PR should merge after fix(dotnet-agent-identity)!: harden issuance and nonce retention #2164. - DVLS takes the rule with the next library package; until then, its conformance run fails
p_metadata_invisible_format_charactersand the U+E007FHellocase. - The closed list leaves out other invisible code points outside
Cf, such as the Hangul fillers (U+115F, U+1160, U+3164, U+FFA0), U+034F and variation selectors, so a name like "admin" followed by U+3164 still looks like "admin". Widening it would be a contract change. UNTESTED:properties added by this PR: none.
This comment was drafted by Claude Code (Claude Opus 5.5).
…ependent The check compares machine_id with /etc/machine-id on Linux, so the fixture now carries the host's value there, and each refused case asserts the format-character error so another check cannot make it pass. Issue: DGW-478 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
An agent reports metadata such as its host name, and an authority shows it to administrators and evaluates the friendly name from it. A value holding bidirectional overrides or zero-width characters can reorder or hide that text, so a device can disguise itself.
The Agent Identity contract now excludes a closed list of invisible format characters from metadata values (U+00AD, U+061C, U+180E, U+200B–U+200F, U+202A–U+202E, U+2060–U+2064, U+2066–U+206F, U+FEFF, U+FFF9–U+FFFB, U+E0001, U+E0020–U+E007F), and the agent removes them before sending. The list is general category
Cfminus the visible marks, fixed as code points so that the agent and every authority agree whatever Unicode version their runtimes ship.invalid_requestat enroll, renew, check-in andHello.DVLS gets the rule with the next authority library package; until it takes that package, its conformance run fails the new refusal checks.
Issue: DGW-478
Issue: DGW-479
Security: yes
🤖 Generated with Claude Code