Repository navigation
Description
Post-merge QA follow-up (#379)
Source: #379 — fix(qa): sanitize evidence_field diagnostics against terminal escape injection
Lens: evidence + test design. The merge does what #378 asked for the evidence_field line: sanitizeDiagnosticField keeps [A-Za-z0-9_.-] and collapses the rest, the regression case reproduces the escape payload and pins the sanitized rendering, and the focused suites plus go vet are stated as run. It does not close the class the issue names in its second acceptance criterion ('The same rule applies to any future diagnostic fields derived from caller-supplied input'): the same code path still prints evidence_detail with %s, and for expected_array rejections that Detail is built from caller-supplied JSON object keys joined with %s, so terminal escape bytes still reach stderr through the sibling diagnostic line. The PR body states that evidence_detail is unaffected 'because it already quotes values with %q', which is not true for the object-keys construction in validateEvidence, and the new test asserts only the field line, so nothing in the suite would catch the surviving path. Test design gap: the new case checks that the sanitized field line is present but never asserts that no raw ESC byte appears anywhere in the stderr stream, which is the property the issue actually asks for.
Blocking findings
- Escape injection survives the fix via evidence_detail built from caller-supplied JSON keys (
internal/qapi/evidence.go): validateEvidence builds the expected_array diagnostic as fmt.Sprintf("top-level must be a JSON array of records, not an object with keys: %s", strings.Join(keys, ", ")), where keys are the caller's own JSON object key names, and internal/cli/doctor.go prints that text through fmt.Fprintf(stderr, "evidence_detail=%s\n", evidenceErr.Detail). fix(qa): sanitize evidence_field diagnostics against terminal escape injection #379 sanitizes only evidenceErr.Field and leaves this pair untouched, so the payload class from evidence_field diagnostic prints attacker-controlled JSON key names unescaped (terminal escape hygiene) #378 still lands raw on the operator terminal. This contradicts the merged PR's own claim that evidence_detail 'already quotes values with %q', and contradicts acceptance criterion 2 of evidence_field diagnostic prints attacker-controlled JSON key names unescaped (terminal escape hygiene) #378: 'The same rule applies to any future diagnostic fields derived from caller-supplied input'. The added test case asserts only that the sanitized evidence_field line is present, so the suite stays green with the leak open.- Failure scenario: Write an evidence file whose top-level JSON is an object with an escape sequence in a key, e.g. {"\u001b[2J\u001b[31mowned\u001b[0m": 1}, and run
cortex qa run --role requirements-analyst --request req.txt --catalog ./catalog --evidence evidence.json. Unmarshalling into []json.RawMessage fails, unmarshalling into map[string]json.RawMessage succeeds, the keys are joined verbatim into Detail, and stderr receivesevidence_detail=top-level must be a JSON array of records, not an object with keys: <ESC>[2J<ESC>[31mowned<ESC>[0m— raw ESC bytes, the exact injection evidence_field diagnostic prints attacker-controlled JSON key names unescaped (terminal escape hygiene) #378 was opened to stop.
- Failure scenario: Write an evidence file whose top-level JSON is an object with an escape sequence in a key, e.g. {"\u001b[2J\u001b[31mowned\u001b[0m": 1}, and run
Acceptance criteria
- Fixed: Escape injection survives the fix via evidence_detail built from caller-supplied JSON keys (
internal/qapi/evidence.go). The scenario below no longer reproduces: Write an evidence file whose top-level JSON is an object with an escape sequence in a key, e.g. {"\u001b[2J\u001b[31mowned\u001b[0m": 1}, and runcortex qa run --role requirements-analyst --request req.txt --catalog ./catalog --evidence evidence.json. Unmarshalling into []json.RawMessage fails, unmarshalling into map[string]json.RawMessage succeeds, the keys are joined verbatim into Detail, and stderr receivesevidence_detail=top-level must be a JSON array of records, not an object with keys: <ESC>[2J<ESC>[31mowned<ESC>[0m— raw ESC bytes, the exact injection evidence_field diagnostic prints attacker-controlled JSON key names unescaped (terminal escape hygiene) #378 was opened to stop.
Activity
barbatdev commentedon Oct 25, 2026
Fleet issue review (glm)
Recommendation: approve
Clear, concrete follow-up: the merged fix for #379 sanitized evidence_field but left the evidence_detail path printing caller-supplied JSON object keys verbatim to stderr, so the terminal-escape injection class from #378 survives. The issue states the problem precisely, cites the exact construction site (validateEvidence in internal/qapi/evidence.go) and the print site (internal/cli/doctor.go), and its single acceptance criterion is a concrete, reproducible scenario (evidence file with an escape sequence in a JSON key, the exact run command, and the raw-ESC stderr output to assert against), which makes it directly testable. Scope is bounded to that one diagnostic path, and both named source files exist on the default branch.
cortex qa run --role requirements-analyst ...command, and the expected raw-ESC stderr line — satisfiable and verifiable by running that scenario.Juan merges; the fleet applies the
status:approvedlabel when a review recommendsapprove.