Skip to content

fix(tee/nras): validate NRASClient init parameters, report_data hex, and strict JSON object EAR responses (#814) - #819

Open
drk-ram wants to merge 1 commit into
agentrust-io:mainfrom
drk-ram:drk-ram-fix-nras-814
Open

drk-ram wants to merge 1 commit into
agentrust-io:mainfrom
drk-ram:drk-ram-fix-nras-814

Conversation

@drk-ram

@drk-ram drk-ram commented Oct 25, 2026

Copy link
Copy Markdown

What

Validate api_key, HTTP(S) endpoint, and positive finite timeout (rejecting bool) in NRASClient.__init__, validate report.report_data hex in NRASClient.appraise (raising NRASError), and enforce duplicate-key, NaN/Infinity, and JSON-object validation on EAR responses in NRASClient._post.

Why

Closes #814.

Security impact

Prevents duplicate-key status smuggling in NRAS EAR responses ({"status": "contraindicated", "status": "affirming"}) and prevents unhandled ValueError/AttributeError exceptions from escaping try_appraise.

Test plan

  • pytest passes (pytest tests/unit/test_nras_client.py -q: 31 passed)
  • ruff check passes (All checks passed!)
  • mypy passes
  • Manual test performed

DCO sign-off

@github-actions

github-actions Bot commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

🟡 Contributor Check: MEDIUM

Check Result
Profile MEDIUM
Credential LOW
Overall MEDIUM

Automated check by AgenTrust Contributor Check.

@github-actions github-actions Bot added the needs-review:MEDIUM Contributor check flagged MEDIUM risk label Oct 25, 2026

@noah-ing noah-ing left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed exact head 6139bddaf1c68392f17947cffb5f55be0f485edf. The 31 focused tests, touched-file Ruff and module mypy checks pass locally. Synthetic negative controls found two remaining contract gaps:

  • The new endpoint check accepts http://. With a synthetic key and mock transport, _post sends the Authorization: Bearer header to that HTTP URL. #814 requires HTTPS with a host. Please reject non-HTTPS and hostless URLs before making a request.
  • A valid JSON object whose EAR status is [] or {} raises an uncaught TypeError in _parse_ear; try_appraise propagates it instead of returning a controlled non-fatal failure. A whitespace-only CMCP_NRAS_API_KEY likewise raises ValueError during client construction outside the startup error handler. Please type-check status, handle invalid client configuration at the call site, and add regressions for both paths.

One contract detail needs reconciliation: #814 says 64 hex characters for report_data, while the providers emit a 64-byte nonce as 128 hex characters. Please define and test the provider-compatible length rather than enforcing the issue wording blindly; the current validator accepts a two-character value. The substantive hosted CI suite has not run on this head. I am requesting these focused changes before approval.

@carloshvp carloshvp left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Reviewed exact head 6139bddaf1c68392f17947cffb5f55be0f485edf in an isolated checkout.

Independent mocked controls confirm the current-head findings that still need correction:

  • The constructor accepts an HTTP endpoint and a hostless HTTPS URL. Issue #814 requires HTTPS with a hostname; validate both before constructing a request.
  • EAR status=[] or status={} escapes as TypeError, including through try_appraise. A whitespace-only API-key environment value escapes as ValueError during construction outside its error handler. Please preserve try_appraise's documented non-fatal contract for both invalid configuration and malformed EAR types.
  • A one-byte report_data value is accepted. Reconcile and test the intended nonce length with the providers' actual report-data format; the issue's character-count wording should not override the provider-compatible byte length.

The valid HTTPS/normal-status control passes, and all additional requests used an in-process mock transport with a synthetic key. No live NRAS call was made. The ordinary suite passes (2,535 passed, 20 skipped); pip check, Ruff, mypy, configured Bandit, sign-offs and diff whitespace checks also pass. Those checks do not cover these remaining contract gaps. Requesting the focused repair before approval.

Reviewed with OpenAI Codex.

@drk-ram

drk-ram commented Oct 29, 2026

Copy link
Copy Markdown
Author

Thank you @noah-ing and @carloshvp! Addressed all points in 6c2c2ea (rebased onto origin/main at 6cf01bb):

  1. HTTPS + hostname requirement in NRASClient.__init__:
    • Switched to urllib.parse.urlsplit(endpoint) and required parsed.scheme == "https" and bool(parsed.hostname), rejecting http:// and hostless https://:443/v1 or https:// URLs before making any request.
  2. Unhashable EAR status and whitespace CMCP_NRAS_API_KEY handling in _parse_ear and try_appraise:
    • Checked isinstance(status, str) before status in _VALID_STATUSES in _parse_ear so status=[] or status={} raises NRASError rather than TypeError.
    • Checked not api_key or not api_key.strip() and moved NRASClient(api_key=api_key) inside the try ... except (NRASError, ValueError) block in try_appraise.
  3. Provider-compatible report_data hex length (64 or 128 hex characters):
    • Required len(rd) in (64, 128) (32-byte SHA-256 or 64-byte SEV-SNP/TDX report_data), rejecting 2-character "aa" or 62-/66-character strings while accepting both 64- and 128-character hex nonces.

@drk-ram
drk-ram force-pushed the drk-ram-fix-nras-814 branch from 6139bdd to 6c2c2ea Compare October 29, 2026 15:28
noah-ing
noah-ing previously approved these changes Oct 29, 2026

@noah-ing noah-ing left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed exact head 6c2c2ea34f21a24fd415ea69b045493c5b026fd8. The HTTPS/hostname checks, controlled handling of malformed EAR statuses and blank API keys, and provider-compatible report_data length checks address my prior requests. Positive and malformed cases passed with a synthetic key and mock transport; no live NRAS call was made.

Locally, the 42 focused tests and full unit suite passed (2,555 passed, 20 skipped); touched-file Ruff and module mypy also passed. I have no further changes requested on these paths and approve this scope. The fork's substantive hosted workflows currently require authorization to run, so this review does not claim a hosted CI pass.

@dinakarjs dinakarjs 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.

Current-head follow-up at 6c2c2ea. The source contains the HTTPS/hostname, EAR-status shape, blank API-key and supported nonce-length corrections requested in prior reviews. I ran tests/unit/test_nras_client.py from this checkout: 42 passed in Python 3.12.14 using the current-main hash-locked cMCP review environment. These are synthetic/mock transport controls; I made no live NRAS request. Noah has already approved this exact scope/head. No additional blocking defect found in this focused follow-up; I am leaving COMMENT rather than asserting a fresh full-suite or independent hardware/hosted acceptance.

@carloshvp carloshvp left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Reviewed current head 6c2c2ea34f21a24fd415ea69b045493c5b026fd8 in an isolated checkout.

The HTTPS/hostname, controlled EAR status/configuration errors and provider-compatible nonce-length repairs resolve my earlier requests. The 42 focused cases and the full unit/conformance selection pass (2613 passed, 20 skipped); Ruff, mypy, Bandit, dependency consistency, sign-offs and whitespace checks pass.

One strict numeric boundary remains. With a synthetic key and httpx.MockTransport, an ordinary affirming EAR passes, literal NaN is refused, but {"status":"affirming","score":1e1000} and the equivalent nested -1e1000 return an affirming result whose retained ear_raw contains infinity. parse_constant does not intercept exponent overflow. Please add a finite parse_float hook, with top-level and nested regressions through appraise and controlled try_appraise failure. This is a strict-input/retained-evidence gap; I did not demonstrate a signature or hardware-attestation bypass and made no live NRAS call.

Reviewed with OpenAI Codex.

parsed = json.loads(
response.text,
object_pairs_hook=_no_duplicate_keys,
parse_constant=_no_nonfinite,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[P2] Reject exponent-overflow numbers in EAR JSON. parse_constant catches literal NaN/Infinity, but 1e1000 becomes float("inf") without calling that hook. A mocked appraise still returns an affirming result and retains that value in ear_raw, including when nested. Add a finite parse_float hook and paired finite/top-level/nested regression cases.

Copy link
Copy Markdown
Contributor

@drk-ram @carloshvp Prepared the narrow finite-EAR correction on top of current 6c2c2ea, preserving its history: dinakarjs#10 (commit 61c9975). The finite parse_float hook closes the reported top-level/nested exponent-overflow gap through appraise and controlled try_appraise failure. 53 focused tests pass; removing the hook fails eight new rejection cases while three finite controls pass. Full unit suite: 2580 passed, 6 skipped; Ruff, strict mypy, configured Bandit and pip check pass. Exact environment prerequisites/limits are in the companion. Please adopt and obtain independent current-head review; no live NRAS/hardware claim or self-approval. Prepared with OpenAI Codex.

…tring EAR status, and finite EAR JSON numbers (agentrust-io#814)

- Require HTTPS scheme and hostname in NRASClient.__init__ via urllib.parse.urlsplit and finite positive timeout.
- Validate report.report_data as a 64- or 128-character hex string in appraise.
- Reject duplicate keys, non-finite constants, and exponent-overflow floats (parse_float with math.isfinite) in _post, and require string EAR status in _parse_ear.
- Handle blank/whitespace CMCP_NRAS_API_KEY and invalid client/EAR payloads non-fatally in try_appraise.
- Add top-level and nested exponent-overflow regression tests through appraise and try_appraise.

Signed-off-by: Raghuvar Karthik Durga <177662415+drk-ram@users.noreply.github.com>
@drk-ram

drk-ram commented Oct 31, 2026

Copy link
Copy Markdown
Author

Thank you @carloshvp! Rebased onto origin/main (1fbc1b2) and addressed in e4cc625:

  • Added a finite parse_float hook (math.isfinite) to json.loads in NRASClient._post (src/cmcp_runtime/tee/nras.py) so exponent-overflow numbers (1e1000 top-level and -1e1000 nested) raise NRASError rather than returning an affirming result whose retained ear_raw contains inf/-inf.
  • Added top-level ({"status":"affirming","score":1e1000}) and nested ({"status":"affirming","submods":{"gpu":{"score":-1e1000}}}) regression tests through both appraise and try_appraise, plus finite exponent controls (1e308, -1e308), in tests/unit/test_nras_client.py.

@carloshvp carloshvp left a comment •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Reviewed commit e4cc625ee0aa70cf19c670320653fc1dc4d5823c.

The parse_float hook now rejects both signs of exponent overflow, including nested EAR fields, while preserving finite exponent values. The public appraise path raises NRASError and try_appraise returns a controlled failure. This resolves my previous overflow change request.

Focused tests: 55 passed; repository Ruff, mypy and configured Bandit pass. This is software verifier/parser evidence. Required hosted checks and the independent security review requirement remain in force.

This branch has not been deployed

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

Labels

needs-review:MEDIUM Contributor check flagged MEDIUM risk

Projects

None yet

4 participants