Skip to content

QA follow-up: fix(qa): sanitize evidence_field diagnostics against terminal escape injection (#379) #387

Open

Description

@barbatdev

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

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 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 receives evidence_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

  1. barbatdev commented on Oct 25, 2026

    @barbatdev
    ContributorAuthor

    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.

    Item Pass Evidence
    clear problem yes The issue states a concrete defect: escape injection survives via evidence_detail built from caller-supplied JSON keys in validateEvidence (internal/qapi/evidence.go) and printed by internal/cli/doctor.go with %s, contradicting criterion 2 of #378.
    acceptance criteria present and testable yes One criterion with a fully specified reproduction: evidence file {"^[[2J^[[31mowned^[[0m": 1}, the exact cortex qa run --role requirements-analyst ... command, and the expected raw-ESC stderr line — satisfiable and verifiable by running that scenario.
    scope bounded yes A single deliverable: close the surviving escape path in the evidence_detail diagnostic derived from caller-supplied keys; no other scope.
    no open questions yes No questions in the body or comments; cause, location, and reproduction are all specified.
    grounded in upstream yes exists on the default branch: internal/qapi/evidence.go = true, internal/cli/doctor.go = true.

    Juan merges; the fleet applies the status:approved label when a review recommends approve.

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions