Skip to content

test(safe-outputs): verify work item rendering fidelity end to end - #1974

Merged
jamesadevine merged 42 commits into
mainfrom
copilot/update-sanitization-code
Sep 1, 2026
Merged

test(safe-outputs): verify work item rendering fidelity end to end#1974
jamesadevine merged 42 commits into
mainfrom
copilot/update-sanitization-code

Conversation

Copilot AI commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Summary

The Markdown sanitizer rewrite changes what ends up in a work item description, but nothing asserted the rendered result — scenarios/work-item.ts only checked title and assignee, so a rendering regression would ship silently.

sanitize_markdown reaches ADO through exactly one path: create-work-item's description, written to System.Description (or Microsoft.VSTS.TCM.ReproSteps for Bugs) with a /multilineFieldsFormat/<field> = Markdown patch. This adds coverage for that path at both the sub-second and against-real-ADO levels.

Shared corpus (single source of truth)

  • scripts/ado-script/src/executor-e2e/scenarios/markdown-rendering-corpus.json holds input (headings, nested lists, GFM + HTML tables with colspan, <details>/<summary>, <kbd>/<sub>/<sup>, autolink, image, and denied constructs interleaved with their fenced-code twins) and expected (the sanitized golden), as line arrays.
  • Rust include_str!s it; the harness imports it. A deliberate rendering change means editing expected in one file — the two layers cannot drift.

Rust

  • src/sanitize/markdown.rs: rendering_corpus test module + golden test asserting sanitize_markdown(input) == expected.
  • src/safe_outputs/create_work_item.rs: wiremock test pinning the default path — the patch carries the sanitized golden and /multilineFieldsFormat/System.Description = Markdown.

Executor E2E

  • create-work-item-rendering (Task) and create-work-item-rendering-bug (Bug) propose the raw corpus, then assert on the stored field:
    1. no <script/onerror=/<iframe/javascript: outside fenced code, while the fenced twins survive verbatim;
    2. multilineFieldsFormat: Markdown;
    3. byte-for-byte equality with the golden.
  • Security runs before the golden so a leak reports as a leak, not a generic mismatch.
  • AdoRest.getWorkItem now surfaces multilineFieldsFormat; new workItemTypeExists lets the Bug scenario SkipError where the type is undefined.

Deviation worth reviewing: ADO does not reliably surface multilineFieldsFormat on read and I could not probe AgentPlayground from the sandbox, so assertion (2) is conditional — it fails when the value is present and not Markdown, and logs a note when absent, with the patch itself pinned by the new Rust test. This is the fallback the plan allowed; if the field does come back on AgentPlayground, the conditional should be tightened to a hard requirement. The optional comment-based renderer probe was skipped, since comments go through sanitize_text, not the changed code.

Test plan

  • cargo test --all-targets (new golden + executor patch tests included)
  • npx vitest run in scripts/ado-script — 1149 tests, including new coverage for golden mismatch, non-Markdown format, leaked construct, mangled fenced code, and the Bug-type skip
  • npm run typecheck; cargo clippy --all-targets shows only a pre-existing warning
  • The E2E scenarios themselves run on the existing PR-validation and daily lanes (allScenarios); no pipeline change needed and they have not been exercised against ADO from here

Copilot AI and others added 30 commits August 16, 2026 08:25
Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com>
Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com>
Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com>
Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com>
Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com>
Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com>
Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com>
Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com>
Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com>
Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com>
Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com>
Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com>
Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com>
Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com>
Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com>
Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com>
Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com>
Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com>
Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com>
Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com>
Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com>
Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com>
Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com>
Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com>
Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com>
Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com>
Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com>
Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com>
Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com>

@github-actions github-actions Bot 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.

Security review

This PR is primarily test coverage for Markdown→work-item rendering fidelity, but it also lands the larger sanitize.rs → allowlist-based sanitize/markdown.rs rewrite that's central to the XSS/injection trust boundary for create-work-item descriptions.

Reviewed the allowlist design (ALLOWED_TAGS, ALLOWED_URL_SCHEMES, scheme_allowed, escape_non_tag_markup, locate_destination/protectable_destination, unescape_ampersands) end to end:

  • The switch from a blocklist (is_dangerous_html_tag) to a closed HTML tag allowlist (ammonia::Builder) is a genuine hardening improvement — unrecognized tags are dropped by construction instead of enumerated.
  • scheme_allowed correctly denies-by-default (only http/https/mailto pass), including disguised schemes with internal whitespace (java\tscript:), since only the outer string is trimmed before comparison.
  • The label/destination-confusion bug already flagged in this thread (locate_destination matching the label text instead of the actual destination for payloads like [(redacted) is fixed in the current diff — the search now starts after the ](/]: terminator, and the new tests (redacts_destination_when_the_label_repeats_it, redacts_reference_definition_when_the_label_repeats_it`) cover exactly that regression.
  • unescape_ampersands only reverses a single && pass on text outside tags and leaves </tag boundaries alone, so it doesn't reopen a path to re-parse dropped markup as a tag.

No new injection or token-scope regression found in this diff. The two open threads from the earlier review pass (destination-matching and workItemTypeExists test coverage) both appear addressed by the referenced follow-up commit. Nothing else here looks merge-blocking from a security standpoint.

🔒 Security review by PR Security Reviewer · auto · 43.9 AIC · ⌖ 14.8 AIC · ⊞ 11.7K
Comment /review to run again

@github-actions github-actions Bot 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.

TypeScript review (scripts/ado-script/)

Reviewed the executor-e2e additions: AdoRest.workItemTypeExists, the new rendering-fidelity scenarios in work-item.ts, and the shared JSON corpus.

  • Error propagation is correct: workItemTypeExists only swallows 404 via allow404, other failures still throw with the HTTP status/body, and this is unit-tested (200/404/403) in the new ado-rest.test.ts.
  • The prior review thread flagged the missing direct unit test for workItemTypeExists — that's now resolved by ado-rest.test.ts.
  • renderingScenario's assert records state.createdId before any fallible check, so cleanup still runs on a golden mismatch — good defensive ordering.
  • Types are sound throughout (no any, no unchecked casts); stored is narrowed with a typeof guard before use, and the optional multilineFieldsFormat is handled without a non-null assertion.

No merge-blocking issues found in this workspace. (A background TypeScript sub-agent pass returned no additional findings.)

🟦 TypeScript code quality review by TypeScript Code Quality Reviewer · auto · 56.2 AIC · ⌖ 14.2 AIC · ⊞ 11.4K
Comment /review to run again

@github-actions github-actions Bot 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.

Solid rendering-fidelity coverage overall — the shared JSON corpus keeps the Rust golden and the executor E2E scenario from drifting, and the ordering of security-check-before-golden-check is well tested (leaked construct, mangled fenced code, non-Markdown format, mismatch all have dedicated cases).

Two prior findings on this PR (the locate_destination label-repeat bug and the missing workItemTypeExists unit test) are already confirmed fixed in later commits — nice.

Three smaller gaps in the new test coverage, none blocking:

  1. The Bug scenario (createBugWorkItemRendering) is only exercised on its failure path (missing-field error) — there's no test proving a successful Bug pass with Microsoft.VSTS.TCM.ReproSteps correctly populated.
  2. The (redacted) URL leak branch in assert(separate from theDENIED_CONSTRUCTS` loop) has no dedicated test.
  3. The new cleanup fallback (state.createdId ?? findWorkItemByTitle(...)) — the exact scenario these tests are designed around (assert throws before recording the id) — is itself untested.

None of these are regressions or weakened assertions; they're coverage gaps in otherwise well-structured new tests.

🧪 Test quality analysis by Test Quality Sentinel · auto · 133.4 AIC · ⌖ 2.04 AIC · ⊞ 11.3K
Comment /review to run again

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3c8e5f36-958b-48ea-99cd-d7c14b5eb8da
@jamesadevine

Copy link
Copy Markdown
Collaborator

/review

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed the test quality analysis.

🧪 Test quality analysis by Test Quality Sentinel

@github-actions github-actions Bot 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.

Test-only PR (executor-E2E + Rust golden coverage for sanitize_markdown rendering fidelity). Checked for the usual drift classes:

  • AGENTS.md: updated to add the new src/sanitize/markdown.rs module — in sync.
  • docs/safe-outputs.md: new Rendering fidelity section documents the two new work-item scenarios and the shared corpus/goldens file — in sync.
  • Codegen: no changes to filter_ir.rs/Fact enum, so types.gen.ts / fact-catalog.gen.json correctly untouched.
  • Lock files: no .github/workflows/*.md changed, so no .lock.yml drift.
  • scripts/ado-script/*.js bundles: absent from the file list as expected (generated, gitignored) — not a finding.
  • Newtypes / src/secure.rs: no new Params identifier fields introduced; create_work_item.rs change is test-only (wiremock assertions), no new raw String path/ref fields.
  • Shared corpus single-source-of-truth: markdown-rendering-corpus.json is include_str!d by Rust and imported by the TS harness, so a golden edit cannot drift between the two layers — good design.

No contract violations found. No blocking issues.

🏗️ Compiler contract review by Compiler Contract Reviewer · auto · 25.1 AIC · ⌖ 3.09 AIC · ⊞ 12.8K
Comment /review to run again

@github-actions github-actions Bot 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.

TypeScript review

Reviewed the executor-e2e additions (ado-rest.ts, work-item.ts, and their tests) for the markdown rendering-fidelity coverage.

Both prior review findings on this diff — the locate_destination search-span bug in src/sanitize/markdown.rs and the missing unit test for workItemTypeExists — are already fixed in follow-up commits (8d0f6f0 per the review thread). No new async/error-handling defects, type-safety holes, or secret-leakage issues found in the new TypeScript: workItemTypeExists/getWorkItem correctly treat multilineFieldsFormat as optional, external JSON payloads are guarded with typeof checks before use, and the rendering scenario propagates errors with clear context. No blocking issues.

🟦 TypeScript code quality review by TypeScript Code Quality Reviewer · auto · 42 AIC · ⌖ 2.46 AIC · ⊞ 11.4K
Comment /review to run again

@github-actions github-actions Bot 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.

This is a security-focused review of a Markdown sanitizer rewrite plus test coverage additions.

Summary

src/sanitize/markdown.rs replaces the previous regex-based HTML denylist (neutralize_dangerous_html, sanitize_url_protocols, event-handler stripping) with a structural, allowlist-based design: pulldown_cmark identifies code spans/fences/autolinks and link/image destinations, and ammonia cleans everything else against a closed tag/attribute allowlist plus a URL-scheme allowlist (http, https, mailto). This is a net security improvement over the prior enumerate-the-bad-patterns approach, since allowlisting is closed by construction rather than requiring every new evasion to be special-cased.

Prior findings already resolved

Both issues raised in earlier review passes on this PR are confirmed fixed in the current diff:

  • locate_destination matching the label instead of the actual destination for repeated-text payloads (e.g. [(redacted) — fixed by searching only after the label terminator (](/]:), verified by the new redacts_destination_when_the_label_repeats_itandredacts_reference_definition_when_the_label_repeats_it` tests.
  • workItemTypeExists lacking direct unit coverage — addressed with ado-rest.test.ts stubbing fetch for 200/404/403.

Review of the new sanitizer

I checked the allowlist config (CLEANER in markdown.rs) for privilege leaks:

  • No scripting tags (script, iframe, object, embed, svg, event-handler attributes) are in ALLOWED_TAGS/tag_attributes()clean_content_tags also drops the content of stripped script/style elements rather than leaving it as escaped text.
  • a only carries href; no target/rel, so link_rel(None) does not reintroduce a tabnabbing vector.
  • img only carries src/alt/width/height — no onerror/onload reachable regardless of what plausible_attributes() (a heuristic only used to decide tag-vs-prose escaping, not a security gate) considers plausible.
  • scheme_allowed() denies by default and correctly treats (host/redacted) as scheme-less (relative), which is consistent with existing behavior and not a new open redirect since no scheme-based execution is possible there.
  • The redaction path only strips the destination, keeping nested elements independently checked, and the whole element is redacted when the destination cannot be located deterministically (fail-closed).

Verdict

This diff does not weaken the security posture — it is a like-for-like (and more robust) reimplementation of sanitize_markdown, is exercised by a large new golden-corpus test suite (markdown-rendering-corpus.json + Rust/TS tests), and both previously flagged issues are fixed. No new merge-blocking findings.

🔒 Security review by PR Security Reviewer · auto · 59.2 AIC · ⌖ 2 AIC · ⊞ 11.7K
Comment /review to run again

@github-actions github-actions Bot 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.

Rust review — no new merge-blocking issues

Reviewed the new src/sanitize/markdown.rs module (allowlist-based Markdown/HTML sanitizer replacing the old regex blocklist) and the sanitize.rs diff. Checked char-boundary safety of bounded(), starts_html_tag, plausible_attributes, mark_blockquotes, unescape_ampersands, and the placeholder restore() round trip against adversarial multi-byte/UTF-8 input, and the deny-by-default scheme_allowed scheme check — no panics or bypasses found. The locate_destination label-repetition bug flagged in an earlier review round is already fixed (commit 8d0f6f0) and the corresponding test (redacts_destination_when_the_label_repeats_it) covers it. The rust-critic sub-agent returned no findings after a full pass on this diff.

No additional Rust-quality concerns to raise; deferring to the Compiler Contract and Security reviewers for anything ado-aw-specific.

🦀 Rust code quality review by Rust Code Quality Reviewer · auto · 52.8 AIC · ⌖ 8.85 AIC · ⊞ 11.5K
Comment /review to run again

@github-actions github-actions Bot 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.

Solid test additions overall — the shared corpus mechanism (Rust include_str! + TS import) is a good way to prevent the two boundaries drifting, and the golden/security/format assertion ordering is well thought out with dedicated tests for each failure mode. Two coverage gaps worth closing before merge:

  1. The security check in renderingScenario.assert has two independent leak-detection branches (DENIED_CONSTRUCTS array vs. the separate (redacted) check), but only one construct (<script`) is ever exercised by a test — a regression in the other three paths would ship silently.
  2. The happy-path Markdown-format test asserts against a lowercased "markdown" fixture rather than the "Markdown" value the executor actually writes, so the realistic value is never exercised.

Both are inline. Nothing merge-blocking.

🧪 Test quality analysis by Test Quality Sentinel · auto · 73 AIC · ⌖ 2.28 AIC · ⊞ 11.3K
Comment /review to run again

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3c8e5f36-958b-48ea-99cd-d7c14b5eb8da
@jamesadevine

Copy link
Copy Markdown
Collaborator

/review

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Rust Code Quality Reviewer completed the Rust code quality review.

🦀 Rust code quality review by Rust Code Quality Reviewer

@github-actions github-actions Bot mentioned this pull request Sep 1, 2026

@github-actions github-actions Bot 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.

TypeScript review (scripts/ado-script)

Reviewed the new AdoRest.workItemTypeExists helper, the JSON-corpus-driven rendering scenarios in work-item.ts, and their tests. No unhandled rejections, swallowed errors, unsafe any/casts on external data, or secret-leak risks found — all async paths are awaited/propagated correctly, and the new REST call uses the existing allow404 pattern consistently with the rest of the file. The one pre-existing gap (workItemTypeExists lacking a direct unit test outside the mocked scenario context) is already covered by an earlier review comment on this PR, so not duplicating it. No merge-blocking issues.

🟦 TypeScript code quality review by TypeScript Code Quality Reviewer · auto · 45.5 AIC · ⌖ 3.85 AIC · ⊞ 11.4K
Comment /review to run again

@github-actions github-actions Bot 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.

Test Quality Review

Strong test suite overall — this is exactly the kind of coverage a rendering-fidelity regression needs.

Strengths:

  • The shared corpus (markdown-rendering-corpus.json) as single source of truth between the Rust golden test and the TS executor E2E is a solid design — a rendering change can't drift between the two layers.
  • Good failure-path coverage in work-item-scenarios.test.ts: format mismatch, denied-construct leakage (script/onerror/iframe/javascript-URL, each parametrized), Bug repro-steps field name mismatch, and the ADO-golden-mismatch-after-createdId-recorded ordering (important for cleanup correctness).
  • src/sanitize/markdown.rs tests are behavioral, not implementation-mirroring: they assert on rendered output for real attack patterns (folded tags, entity-encoded schemes, nested destinations, forged sentinels) rather than internal parser state. The idempotency and bounded-scan (linear time on adversarial input) tests are good defenses against regressions in a security-sensitive rewrite.
  • ado-rest.test.ts covers the new workItemTypeExists happy/404/403 paths, addressing the gap flagged in an earlier review round.
  • The sanitize.rs test diff removing old blocklist-specific tests is expected churn from the allowlist rewrite, not a weakening — assertions were adapted to the new tag-allowlist semantics, not silently dropped.

One gap found: see inline comment on work-item.ts — the format === undefined fallback branch (org doesn't surface multilineFieldsFormat) has no test distinguishing it from the "format is defined but wrong" failure case.

Non-blocking overall.

🧪 Test quality analysis by Test Quality Sentinel · auto · 80.2 AIC · ⌖ 2.23 AIC · ⊞ 11.3K
Comment /review to run again

@github-actions github-actions Bot 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.

Security review - no diff-level regressions found

This diff is test-only in production impact: it adds a Markdown-aware sanitizer rewrite (src/sanitize/markdown.rs) with an allowlist-based HTML/URL scheme policy, plus a shared rendering corpus and executor-e2e/Rust golden tests for create-work-item's description field. I traced the two threat-model areas most relevant to this PR:

  • Injection into generated pipelines: not touched by this diff - no new bash:/##vso[/template-expression sinks.
  • Token scope: ado-rest.ts's new workItemTypeExists reuses the existing authenticated request() helper; no new token/secret paths.
  • URL scheme / HTML allowlist correctness: the two prior review findings on this PR (destination-lookup anchoring bypass in locate_destination, and missing unit coverage for workItemTypeExists) are both already fixed and confirmed in the current HEAD (commits 8d0f6f0 and later) - locate_destination now searches after the label terminator, and ado-rest.test.ts covers 200/404/403 cases.
  • Reviewed scheme_allowed, starts_html_tag, plausible_attributes, and the placeholder/sentinel scheme for smuggling/redaction bypass: sentinels are stripped from input before use, redaction happens before the HTML cleaner runs, and destinations carrying angle brackets fall through to the allowlist rather than being copied verbatim - no bypass found.

No new merge-blocking findings. Good test-coverage work tying the Rust golden and executor-e2e ADO round-trip goldens to one shared corpus file.

🔒 Security review by PR Security Reviewer · auto · 57.5 AIC · ⌖ 3.08 AIC · ⊞ 11.7K
Comment /review to run again

@github-actions github-actions Bot 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.

Rust review — no merge-blocking issues

Scope: src/sanitize/markdown.rs (new), src/sanitize.rs, src/safe_outputs/create_work_item.rs (test-only addition).

The rust-critic sub-agent produced no findings after two turns (idle both times, no JSONL output) — noting per the contract rather than treating silence as a pass.

My own pass over the new markdown sanitizer found no unaddressed correctness or error-handling defects. locate_destination mislocating a destination for nested-link source spans — the one real bug in this area — was already flagged in a prior review round and is confirmed fixed in 8d0f6f0. The create_work_item.rs change is test-only. Error handling in the new module is sound (unwrap/expect are confined to the #[cfg(test)] corpus loader); Option-returning helpers (locate_destination, protectable_destination) degrade safely on miss per their doc comments, and the module is exercised by an extensive adversarial/idempotency test suite plus a shared golden corpus.

No inline comments to post.

🦀 Rust code quality review by Rust Code Quality Reviewer · auto · 76.7 AIC · ⌖ 2.62 AIC · ⊞ 11.5K
Comment /review to run again

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3c8e5f36-958b-48ea-99cd-d7c14b5eb8da
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3c8e5f36-958b-48ea-99cd-d7c14b5eb8da
@jamesadevine

Copy link
Copy Markdown
Collaborator

/review

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Rust Code Quality Reviewer completed the Rust code quality review.

🦀 Rust code quality review by Rust Code Quality Reviewer

@github-actions github-actions Bot 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.

Reviewed the TypeScript changes in scripts/ado-script/executor-e2e/ (ado-rest.ts, work-item.ts, and the new unit tests). Async/error handling is sound: workItemTypeExists correctly distinguishes 404→false from other failures (still throws), all scenario callbacks are properly awaited, and the security/format/golden assertions in renderingScenario fail loudly with actionable messages rather than swallowing errors. No unhandled rejections, no any leakage, and no secrets touch these code paths.

The only pattern worth noting (not new to this PR) is that AdoRest.request<T>() casts the parsed JSON body to T without runtime shape validation — workItemTypeExists inherits that from the shared helper along with every other method in the class, so it isn't a regression introduced here.

Prior review feedback on this file set (the locate_destination match-span bug and the missing workItemTypeExists unit test) has already been addressed in this PR's history. No new blocking issues found.

🟦 TypeScript code quality review by TypeScript Code Quality Reviewer · auto · 50 AIC · ⌖ 2.19 AIC · ⊞ 11.4K
Comment /review to run again

@github-actions github-actions Bot 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.

Security review: no regressions found

This PR (test(safe-outputs): verify work item rendering fidelity end to end) is test-only. It adds:

  • a shared rendering-fidelity corpus consumed by both Rust unit tests and the executor E2E create-work-item-rendering(-bug) scenarios,
  • a workItemTypeExists helper and multilineFieldsFormat field on getWorkItem for assertions,
  • a wiremock test pinning the create-work-item PATCH body (title + sanitized description + multilineFieldsFormat=Markdown).

The src/sanitize.rs diff shown is the prior allowlist-based rewrite of sanitize_markdown (already reviewed/fixed in earlier commits per pr-review-comments.json), not new production logic in this PR. The new ALLOWED_TAGS/ALLOWED_URL_SCHEMES/KNOWN_TAGS policy in src/sanitize/markdown.rs is deny-by-default (fixed tag allowlist via ammonia, http/https/mailto only for URLs, script/style content stripped), and locate_destination searches only after the label terminator, consistent with the fix already applied for the prior finding on this line.

No injection into generated pipeline YAML, no token-scope changes, no path-traversal-relevant code, and no weakened validation — this diff is security-neutral.

🔒 Security review by PR Security Reviewer · auto · 39.7 AIC · ⌖ 4.41 AIC · ⊞ 11.7K
Comment /review to run again

@github-actions github-actions Bot 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.

Rust review — no merge-blocking issues found

Reviewed the Rust portion of this PR: src/sanitize.rs, the new src/sanitize/markdown.rs (allowlist-based Markdown/HTML sanitizer), and the wiremock test added to src/safe_outputs/create_work_item.rs.

Note on process: the rust-critic sub-agent was launched in the background and given a blocking wait (150s) plus one follow-up nudge, but returned no parseable JSONL findings either time. Its output is discarded per the sub-agent contract; the assessment below is my own pass only.

Findings: none rise to merge-blocking. The two prior review threads on locate_destination (destination-search starting point) and workItemTypeExists (missing direct unit test) are already resolved per the fix commits referenced in the existing PR comments, and the fixes are reflected in this diff (search now starts after ](/]:, and a dedicated ado-rest.test.ts was added).

Spot-checked for common defect classes and found the new module handles them correctly:

  • Range arithmetic in protectable_destination (range.start - 1) is guarded by short-circuit evaluation on an empty-string ends_with check, so no underflow panic is reachable.
  • bounded()'s char-boundary walk-back cannot underflow past 0 since byte offset 0 is always a valid boundary.
  • The allowlist-first HTML design (closed by construction) is a solid improvement over the previous denylist, and the idempotency test (is_idempotent) plus the shared rendering-corpus golden test give good regression coverage for a security-sensitive sanitizer.

No inline comments to add.

🦀 Rust code quality review by Rust Code Quality Reviewer · auto · 57.7 AIC · ⌖ 2.79 AIC · ⊞ 11.5K
Comment /review to run again

@github-actions github-actions Bot 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.

Overall solid test coverage for the new rendering-fidelity path (shared corpus golden test in Rust, wiremock pin on the executor patch, and layered security/format/byte-equality assertions in the E2E scenario). One gap: the Bug-type test only exercises the error path for Microsoft.VSTS.TCM.ReproSteps, never the success path — see inline comment.

🧪 Test quality analysis by Test Quality Sentinel · auto · 58.8 AIC · ⌖ 2.33 AIC · ⊞ 11.3K
Comment /review to run again

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3c8e5f36-958b-48ea-99cd-d7c14b5eb8da
@jamesadevine
jamesadevine merged commit 93643cc into main Sep 1, 2026
23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants