fix(sentinel): real keyed signatures, verify traces before scoring, escape dashboard XSS#13
Merged
Merged
Conversation
…scape dashboard XSS Three fail-closed, env-configurable security fixes for the sentinel integration: 1. Incident signatures are now a real keyed HMAC-SHA256 over the canonical report JSON, using a secret from SENTINEL_SIGNING_KEY. The previous value was base64(sha256(payload) + b"signed") with no key, which any party could recompute. If no key is configured, signing fails closed: the report is marked "signature_status": "unsigned" and carries no signature rather than an authentic-looking value. /verify uses hmac.compare_digest against the keyed HMAC and returns UNVERIFIABLE when no key is set. 2. Incoming TRACE claims are verified before they are scored or enforced. ingest_trace, the /evaluate endpoint, and the CLI fleet path now run a verification gate that checks the Ed25519 signature against a trusted key from TRACE_TRUSTED_JWK (using agentrust_trace.verify_record when available, otherwise a minimal Ed25519 check). The trusted key is the configured one, never the key embedded in the record. Unsigned or invalid traces are rejected unless SENTINEL_ALLOW_UNVERIFIED=1 is set, which logs a loud warning on every use. Default is reject. 3. The enforcement dashboard no longer interpolates trace-derived fields (agent_id, claim.reason, event.description, detection_type, claim_id) into innerHTML or inline on* handlers unescaped. All such values are HTML-escaped via an esc() helper, untrusted values are carried on data-* attributes, and actions are wired with addEventListener instead of inline onclick. Adds tests/test_security.py covering keyed signing and tamper/wrong-key/no-key cases, trace acceptance/rejection at the module, ingest, and endpoint level, and source-level assertions that the dashboard render path escapes untrusted fields. Adds cryptography to requirements. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Security hardening for the
sentinelintegration. Three fail-closed fixes, each configurable via environment variables. No behavior change when the relevant env vars are set; default posture is to reject/refuse rather than fabricate trust.1. Real keyed incident signatures (fail closed)
Incident reports were "signed" with
base64(sha256(payload) + b"signed")— keyless, so any party could recompute it, and/verifyrecomputed the same public value. Replaced with HMAC-SHA256 over the canonical report JSON using a secret fromSENTINEL_SIGNING_KEY."signature_status": "unsigned"and no signature, rather than a value that merely looks signed./verifynow verifies the keyed HMAC withhmac.compare_digest, and returnsUNVERIFIABLEwhen no key is set.2. Verify TRACE claims before scoring/enforcing (fail closed)
ingest_trace, the/evaluateendpoint, and the CLI fleet path fed unverified input straight into scoring and enforcement. Added a verification gate that checks the Ed25519 signature against a trusted key fromTRACE_TRUSTED_JWK(an OKP/Ed25519 public JWK as JSON), usingagentrust_trace.verify_recordwhen the package is importable, otherwise a minimal Ed25519 check.403on the endpoint). The only bypass isSENTINEL_ALLOW_UNVERIFIED=1, which logs a loud warning on every use. Default is reject.3. Escape dashboard DOM-XSS
dashboard.htmlinterpolated posted-trace fields (agent_id,claim.reason,event.description,detection_type,claim_id) intoinnerHTMLand inlineonclickhandlers unescaped. All trace-derived strings are now HTML-escaped via anesc()helper, untrusted values are carried ondata-*attributes, and actions are wired withaddEventListenerinstead of inlineonclick.Tests
Adds
sentinel/tests/test_security.py(18 tests, all passing locally on Python 3.12):ingest_trace, and/evaluateendpoint level;SENTINEL_ALLOW_UNVERIFIED=1opt-out logs a warning./verifyround-trips VERIFIED, detects TAMPERED, and returns UNVERIFIABLE without a key.Both the
agentrust_trace-installed path and the minimal-fallback path were exercised and pass.cryptographyadded torequirements.txt. README andintegration.yamlupdated to document the env vars and the fail-closed behavior.🤖 Generated with Claude Code