Skip to content

fix: preserve template delimiters inside fenced code blocks in safe-output sanitizer#28736

Merged
pelikhan merged 8 commits intomainfrom
copilot/fix-safe-output-sanitizer
Apr 27, 2026
Merged

fix: preserve template delimiters inside fenced code blocks in safe-output sanitizer#28736
pelikhan merged 8 commits intomainfrom
copilot/fix-safe-output-sanitizer

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 27, 2026

neutralizeTemplateDelimiters was applying regex replacements globally, including inside fenced code blocks and inline code spans. This corrupted GitHub suggestion block content — e.g. {{fleet-server}} became \{\{fleet-server}} in the patch, causing "Commit suggestion" to write the backslashes literally into the file.

Changes

  • sanitize_content_core.cjs — Refactored neutralizeTemplateDelimiters to pass the escaping logic through applyToNonCodeRegions, which already skips fenced blocks and inline code spans. A Set deduplicates core.info log calls across multiple non-code segments. Removed all typeof core !== "undefined" guards throughout the file — core is always available as a global or shim in every execution environment.

  • sanitize_content.test.cjs — Replaced the test asserting (now-incorrect) escaping inside inline code with four targeted tests:

    • inline code spans preserved verbatim
    • fenced code blocks preserved verbatim
    • suggestion fenced blocks preserved verbatim ← primary regression case
    • template delimiters in prose still escaped (regression guard)
  • sanitize_content_core_parser.test.cjs (new) — Extensive unit and fuzz tests for the core parser helpers (getFencedCodeRanges, applyFnOutsideInlineCode, applyToNonCodeRegions) with no mocks. 63 tests covering:

    • Backtick and tilde fences, info strings (language tags), suggestion blocks
    • Unclosed fences (safe fallback), closing fence length ≥ opener, fence-character mismatch
    • Adjacent/consecutive blocks, CRLF line endings, indented fences
    • Single/double/triple backtick inline code spans, mismatched counts, nested backticks
    • Fuzz-style invariants: identity transform, fn never sees fenced content, no crash on adversarial inputs (200-backtick fences, Unicode, null bytes, 10 000-char strings)
    • global.core restored after each test to prevent cross-suite pollution

Example

```suggestion
Refer to [Advanced {{fleet-server}} options](/ref.md).
```

Before: {{fleet-server}}\{\{fleet-server}} (patch corrupted)
After: {{fleet-server}} preserved verbatim inside the fence

…utput sanitizer

The `neutralizeTemplateDelimiters` function now uses `applyToNonCodeRegions` so
that `{{`, `${`, `<%=`, `{#`, and `{%` inside fenced code blocks (including GitHub
`suggestion` blocks) and inline code spans are no longer escaped.

This fixes the bug where suggesting documentation changes that include valid
template-like syntax (e.g. Elastic `{{fleet-server}}`) would corrupt the patch
when a reviewer clicked "Commit suggestion".

Updated tests:
- Replaced the "should escape template delimiters in code blocks" test (which
  asserted the now-incorrect behaviour) with four targeted tests:
  - inline code spans are preserved
  - fenced code blocks are preserved
  - `suggestion` fenced blocks are preserved  ← main regression case
  - template delimiters outside code blocks are still escaped

Agent-Logs-Url: https://github.com/github/gh-aw/sessions/b3d5dfb0-f0a0-4bc0-8294-ec87a63672bd

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix safe-output sanitizer to not escape template delimiters fix: preserve template delimiters inside fenced code blocks in safe-output sanitizer Apr 27, 2026
Copilot AI requested a review from pelikhan April 27, 2026 13:56
Copy link
Copy Markdown
Collaborator

@pelikhan pelikhan left a comment

Choose a reason for hiding this comment

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

@copilot core functions are always available (or a shim is loaded), remove check for core = undefined

core is always available as a global (or a shim is loaded), so the
typeof core !== "undefined" guards are unnecessary.

Agent-Logs-Url: https://github.com/github/gh-aw/sessions/9b0d87f4-8e26-4ea0-9f21-395bf174b0e4

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 27, 2026

@copilot core functions are always available (or a shim is loaded), remove check for core = undefined

Done in fe01828 — removed all typeof core !== "undefined" guards from neutralizeTemplateDelimiters.

@pelikhan
Copy link
Copy Markdown
Collaborator

@copilot add extensive unit tests and fuzz tests for parser (no mocks)

…, applyFnOutsideInlineCode, applyToNonCodeRegions)

63 new tests covering:
- getFencedCodeRanges contract (backtick/tilde fences, info strings,
  suggestion blocks, unclosed fences, closing fence length, fence
  character mismatch, adjacent/consecutive blocks, CRLF)
- applyFnOutsideInlineCode inline code span handling (single/double/triple
  backticks, mismatched counts, nested backticks, very long spans,
  many spans in a row)
- applyToNonCodeRegions mixed code region combinations
- Fuzz-style invariant tests (identity transform, fn never sees fenced
  content, no crash on adversarial inputs, fn never receives non-strings)
- Edge cases (empty fence, fence-only document, Windows CRLF, Unicode)

No mocks; core is stubbed as a plain object.

Agent-Logs-Url: https://github.com/github/gh-aw/sessions/ac84c1d9-91a1-4cd1-a204-871dcd17d5a2

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 27, 2026

@copilot add extensive unit tests and fuzz tests for parser (no mocks)

Added in f9cf5e1 — new file sanitize_content_core_parser.test.cjs with 63 tests (no mocks; core is a plain stub object) covering:

  • getFencedCodeRanges contract — backtick/tilde fences, info strings (language tags), suggestion blocks, unclosed fences, closing fence length ≥ opener, fence-character mismatch, adjacent/consecutive blocks, CRLF, indented fences
  • applyFnOutsideInlineCode — single/double/triple backtick spans, mismatched counts, nested backticks, very long spans (5000 chars), 50 spans in a row, multi-line spans
  • applyToNonCodeRegions mixed regions — inline code inside/before/after fenced blocks, identity transform preserves input exactly, empty-string fn, chunk wrapping
  • Fuzz-style invariants — identity transform over 20+ seed cases, fn never receives fenced block content, no crash on adversarial inputs (200-backtick fences, unicode, null bytes, 10 000-char strings), fn always receives strings

@pelikhan pelikhan marked this pull request as ready for review April 27, 2026 14:55
Copilot AI review requested due to automatic review settings April 27, 2026 14:55
@github-actions github-actions Bot mentioned this pull request Apr 27, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes an output-sanitization regression where template delimiter escaping was corrupting content inside markdown code regions (notably GitHub suggestion blocks), leading to backslashes being committed literally when applying suggestions.

Changes:

  • Refactors neutralizeTemplateDelimiters to escape only in non-code markdown regions via applyToNonCodeRegions, and dedupes template-detection logs.
  • Updates/expands sanitize_content.test.cjs to ensure inline code, fenced blocks, and suggestion fences are preserved verbatim while prose is still escaped.
  • Adds a new sanitize_content_core_parser.test.cjs suite with extensive unit + fuzz-style coverage for the markdown code-region helpers.
Show a summary per file
File Description
actions/setup/js/sanitize_content_core.cjs Routes template-delimiter escaping through the non-code-region parser to avoid modifying fenced/inline code.
actions/setup/js/sanitize_content.test.cjs Replaces prior expectations with targeted regression tests for code-region preservation and prose escaping.
actions/setup/js/sanitize_content_core_parser.test.cjs Adds broad unit/fuzz coverage for fenced/inline code parsing and transformation boundaries.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 3/3 changed files
  • Comments generated: 4

Comment on lines +601 to +605
it("multi-line inline code (inline code spans do not cross line boundaries in CommonMark)", () => {
// In CommonMark, inline code CAN span lines. Verify no crash and sensible output.
const result = applyToNonCodeRegions("`line1\nline2`", s => s.toUpperCase());
expect(typeof result).toBe("string");
});
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

The test comment is internally inconsistent about CommonMark behavior: it says inline code spans do not cross line boundaries, then immediately says they can span lines. Please clarify/correct the comment to avoid future confusion when maintaining the parser.

Copilot uses AI. Check for mistakes.
Comment on lines +771 to +779
if (/\{\{/.test(result)) {
if (!detectedTypes.has("jinja2")) {
detectedTypes.add("jinja2");
core.info("Template syntax detected: Jinja2/Liquid double braces {{");
}
result = result.replace(/\{\{/g, "\\{\\{");
}
result = result.replace(/\{\{/g, "\\{\\{");
}

// Escape ERB delimiters: <%= ... %>
// Replace <%= with \<%= to prevent ERB evaluation
if (/<%=/.test(result)) {
templatesDetected = true;
if (typeof core !== "undefined" && core.info) {
core.info("Template syntax detected: ERB delimiter <%=");
// Escape ERB delimiters: <%= ... %>
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

neutralizeTemplateDelimiters now calls core.info / core.warning unconditionally. This will throw a ReferenceError if core is not present in the global scope (e.g., when importing sanitize_content_core.cjs in a plain Node context without setupGlobals()/shim.cjs). Other functions in this file still guard core access via typeof core !== "undefined", so this introduces an inconsistency and a potential runtime regression when template patterns are present. Consider restoring the typeof core !== "undefined" && core.* guards here, or consistently referencing global.core with a safe fallback/no-op logger.

Copilot uses AI. Check for mistakes.
Comment on lines +19 to +29
beforeEach(async () => {
// Set up a minimal stub so code that calls core.* doesn't throw.
// We deliberately avoid vi.fn() to keep tests mock-free.
global.core = {
info: () => {},
warning: () => {},
debug: () => {},
error: () => {},
};

const mod = await import("./sanitize_content_core.cjs");
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

This test file assigns global.core in beforeEach but never restores/deletes it. Leaving a modified global behind can create order-dependent interactions with other test files (many of which explicitly assert/assume global.core is unset between suites). Consider saving the previous global.core and restoring it in an afterEach/afterAll.

Copilot uses AI. Check for mistakes.
Comment on lines +43 to +58
/**
* Helper: verify that a transform fn is NOT called on regions inside a
* fenced block and IS called on regions outside it.
*/
function verifyFences(input, expectedOutside, expectedInsideRaw) {
const calls = [];
applyToNonCodeRegions(input, chunk => {
calls.push(chunk);
return chunk;
});

// The raw fence text should appear verbatim in the result
const result = applyToNonCodeRegions(input, chunk => chunk.toUpperCase());
return { result, calls };
}

Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

verifyFences (and its parameters expectedOutside, expectedInsideRaw) is defined but never used, and the parameters are unused even within the function. This adds dead code/noise to the test suite; consider removing the helper or using it in the fence tests so it contributes to coverage.

Suggested change
/**
* Helper: verify that a transform fn is NOT called on regions inside a
* fenced block and IS called on regions outside it.
*/
function verifyFences(input, expectedOutside, expectedInsideRaw) {
const calls = [];
applyToNonCodeRegions(input, chunk => {
calls.push(chunk);
return chunk;
});
// The raw fence text should appear verbatim in the result
const result = applyToNonCodeRegions(input, chunk => chunk.toUpperCase());
return { result, calls };
}

Copilot uses AI. Check for mistakes.
@github-actions
Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 71/100

⚠️ Acceptable — high behavioral quality with a test-inflation note

Metric Value
New/modified tests analyzed 50 (of 68 total — see sampling note)
✅ Design tests (behavioral contracts) 50 (100%)
⚠️ Implementation tests (low value) 0 (0%)
Tests with error/edge cases 35 (70%)
Duplicate test clusters 0
Test inflation detected ⚠️ Yes — sanitize_content_core_parser.test.cjs (625 lines) vs sanitize_content_core.cjs (61 new lines) = ~10× ratio
🚨 Coding-guideline violations None

⚠️ Sampling applied — analyzed the first 50 of 68 test functions. Prioritized newly added tests.


Test Classification Details

View all 50 classified tests
Test File Classification Notes
should preserve template delimiters inside inline code spans sanitize_content.test.cjs ✅ Design Verifies observable output of behavioral fix
should preserve template delimiters inside fenced code blocks sanitize_content.test.cjs ✅ Design Direct regression test for the PR's fix
should preserve template delimiters inside GitHub suggestion blocks sanitize_content.test.cjs ✅ Design Real-world case with suggestion info string
should still escape template delimiters outside code blocks sanitize_content.test.cjs ✅ Design Verifies the positive case is not broken
empty string returns empty string sanitize_content_core_parser.test.cjs ✅ Design Edge: empty input
null returns empty string sanitize_content_core_parser.test.cjs ✅ Design Edge: null guard
non-string truthy value is returned as-is sanitize_content_core_parser.test.cjs ✅ Design Edge: type coercion guard
plain text with no fences applies fn to everything sanitize_content_core_parser.test.cjs ✅ Design Happy path
backtick-fenced block is preserved verbatim sanitize_content_core_parser.test.cjs ✅ Design Core contract
tilde-fenced block is preserved verbatim sanitize_content_core_parser.test.cjs ✅ Design Alternate fence syntax
fenced block with info string is preserved verbatim sanitize_content_core_parser.test.cjs ✅ Design Language-tagged fences
suggestion block is preserved verbatim sanitize_content_core_parser.test.cjs ✅ Design PR-specific regression test
multiple fenced blocks are each preserved verbatim sanitize_content_core_parser.test.cjs ✅ Design Multi-block boundary
longer opening fence requires longer closing fence sanitize_content_core_parser.test.cjs ✅ Design CommonMark fence-length invariant
unclosed fenced block treats rest as code (safe fallback) sanitize_content_core_parser.test.cjs ✅ Design Edge: security-relevant safe fallback
fence must start at beginning of indented line sanitize_content_core_parser.test.cjs ✅ Design CommonMark 3-space indent rule
backtick fence is not closed by tilde fence sanitize_content_core_parser.test.cjs ✅ Design Cross-type fence isolation
tilde fence is not closed by backtick fence sanitize_content_core_parser.test.cjs ✅ Design Cross-type fence isolation
content between two fenced blocks is transformed sanitize_content_core_parser.test.cjs ✅ Design Interstitial prose contract
text before first fence is transformed sanitize_content_core_parser.test.cjs ✅ Design Boundary case
text after last fence is transformed sanitize_content_core_parser.test.cjs ✅ Design Boundary case
no trailing newline – unclosed (safe) sanitize_content_core_parser.test.cjs ✅ Design Edge: missing trailing newline
empty fenced block is preserved verbatim sanitize_content_core_parser.test.cjs ✅ Design Edge: zero-content block
fence-only document – fn not called at all sanitize_content_core_parser.test.cjs ✅ Design Edge: fn call count guarantee
adjacent fenced blocks with no prose between them sanitize_content_core_parser.test.cjs ✅ Design Edge: no-prose boundary
single-backtick inline code span is preserved verbatim sanitize_content_core_parser.test.cjs ✅ Design Core inline-code contract
double-backtick inline code span is preserved verbatim sanitize_content_core_parser.test.cjs ✅ Design Multi-backtick variant
triple-backtick inline code is preserved verbatim sanitize_content_core_parser.test.cjs ✅ Design Triple-backtick inline edge
inline code span containing template delimiters is preserved sanitize_content_core_parser.test.cjs ✅ Design Core PR contract (inline version)
backtick with no closing match treated as literal (fn applied) sanitize_content_core_parser.test.cjs ✅ Design Edge: unmatched backtick
multiple inline code spans in one line are each preserved sanitize_content_core_parser.test.cjs ✅ Design Multi-span boundary
mismatched backtick counts: single not closed by double sanitize_content_core_parser.test.cjs ✅ Design CommonMark backtick-count rule
double-backtick span closes only with double-backtick sanitize_content_core_parser.test.cjs ✅ Design Matching-count invariant
inline code containing single backtick via double-backtick sanitize_content_core_parser.test.cjs ✅ Design CommonMark escaping edge
empty inline code span is preserved sanitize_content_core_parser.test.cjs ✅ Design Edge: empty span
consecutive inline code spans with content between sanitize_content_core_parser.test.cjs ✅ Design Multi-span interstitial
inline code at start of string sanitize_content_core_parser.test.cjs ✅ Design Boundary
inline code at end of string sanitize_content_core_parser.test.cjs ✅ Design Boundary
entire string is inline code sanitize_content_core_parser.test.cjs ✅ Design Edge: fn never called
inline code inside fenced block is entirely preserved sanitize_content_core_parser.test.cjs ✅ Design Mixed-mode contract
fn is idempotent (identity transform) sanitize_content_core_parser.test.cjs ✅ Design Invariant: no mutation without fn
function returning empty string removes all prose sanitize_content_core_parser.test.cjs ✅ Design Invariant: only prose is affected
identity transform preserves input exactly (fuzz) sanitize_content_core_parser.test.cjs ✅ Design Fuzz: identity invariant over 30+ seeds
fenced block content never passed to fn (fuzz) sanitize_content_core_parser.test.cjs ✅ Design Fuzz: core security invariant
result is a string for all seed inputs sanitize_content_core_parser.test.cjs ✅ Design Fuzz: type safety
adversarial inputs – no crash sanitize_content_core_parser.test.cjs ✅ Design Edge: robustness / DoS hardening
fn called with strings only (fuzz) sanitize_content_core_parser.test.cjs ✅ Design Fuzz: fn contract
fence on very first line with no preceding text sanitize_content_core_parser.test.cjs ✅ Design Boundary
fence character must appear ≥3 times to be valid sanitize_content_core_parser.test.cjs ✅ Design CommonMark minimum-fence rule
fenced block with Windows-style CRLF line endings sanitize_content_core_parser.test.cjs ✅ Design Edge: cross-platform line endings

Flagged Tests — Requires Review

No tests were flagged. All 50 analyzed tests verify observable behavioral contracts.


Notes

⚠️ Test Inflation — sanitize_content_core_parser.test.cjs

The new test file adds 625 lines against 61 new lines in sanitize_content_core.cjs — roughly a 10× ratio, well above the 2:1 threshold that triggers the inflation flag. This reduces the score from 81 to 71 but does not fail the check.

Context: The inflation is understandable here. The production change was a targeted behavioral fix (preserve template delimiters inside code regions), but the tests cover the full CommonMark fenced-code-block parser contract plus fuzz/property-based invariants that provide lasting regression value. The intent appears to be establishing a comprehensive test baseline for a security-relevant parser, not padding line counts.

Recommendation: No action required for this PR, but consider whether a subset of the fuzz seed cases could be consolidated into a table-driven loop to reduce line count without losing coverage.


Language Support

Tests analyzed:

  • 🟨 JavaScript (*.test.cjs): 50 tests (vitest) — sanitize_content.test.cjs (4 new/modified) + sanitize_content_core_parser.test.cjs (64 new, 46 analyzed)
  • 🐹 Go (*_test.go): 0 tests in this PR

Verdict

Check passed. 0% of new tests are implementation tests (threshold: 30%). All analyzed tests verify observable behavioral contracts. No coding-guideline violations detected.


📖 Understanding Test Classifications

Design Tests (High Value) verify what the system does:

  • Assert on observable outputs, return values, or state changes
  • Cover error paths and boundary conditions
  • Would catch a behavioral regression if deleted
  • Remain valid even after internal refactoring

Implementation Tests (Low Value) verify how the system does it:

  • Assert on internal function calls (mocking internals)
  • Only test the happy path with typical inputs
  • Break during legitimate refactoring even when behavior is correct
  • Give false assurance: they pass even when the system is wrong

Goal: Shift toward tests that describe the system's behavioral contract — the promises it makes to its users and collaborators.

References: §25002377019

🧪 Test quality analysis by Test Quality Sentinel · ● 688.9K ·

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

✅ Test Quality Sentinel: 71/100. Test quality is acceptable — 0% of new tests are implementation tests (threshold: 30%). All 50 analyzed tests verify observable behavioral contracts of the markdown code-block parser. Minor note: test file sanitize_content_core_parser.test.cjs shows a ~10× line ratio vs the production file, which is above the 2:1 inflation threshold; however the tests establish a comprehensive CommonMark-parser contract baseline (including fuzz/property-based invariants) that provides lasting regression value. No coding-guideline violations detected.

@pelikhan
Copy link
Copy Markdown
Collaborator

@copilot review all comments

- Restore typeof core !== "undefined" guards in neutralizeTemplateDelimiters
  (core.info x5, core.warning) to match the rest of the file and prevent
  ReferenceError when running in a plain Node context
- Add afterEach to delete global.core in the parser test suite so it does
  not leak into other test files
- Remove unused verifyFences helper function (dead code)
- Fix misleading test name/comment that contradicted itself about whether
  inline code spans can cross line boundaries (they can per CommonMark §6.11)

Agent-Logs-Url: https://github.com/github/gh-aw/sessions/72c80953-78ef-4fc6-9f8a-3b139a884202

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 27, 2026

@copilot review all comments

All four review comments addressed in f8a9047:

  1. core guards — restored typeof core !== "undefined" && core.* guards around all core.info/core.warning calls in neutralizeTemplateDelimiters, consistent with the rest of the file.
  2. global.core leak — added afterEach(() => { delete global.core; }) to the parser test suite.
  3. Unused verifyFences helper — removed the dead code.
  4. Contradictory comment — renamed the test and rewrote the comment to accurately reflect CommonMark §6.11 (inline code spans can span line endings).

@pelikhan
Copy link
Copy Markdown
Collaborator

@copilot remove all core guards, a shim is loaded in node.js environments

A shim is always loaded in Node.js environments, so the guards are
unnecessary. Removed all 13 remaining typeof core !== "undefined"
guard wrappers across the file (URL redaction, mention escaping,
template delimiter detection, GitHub reference filtering).

Agent-Logs-Url: https://github.com/github/gh-aw/sessions/d67f99b2-824d-43ca-a316-13659e8da6a5

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 27, 2026

@copilot remove all core guards, a shim is loaded in node.js environments

Done in fd30d3e — removed all remaining typeof core !== "undefined" guards from sanitize_content_core.cjs (URL redaction, mention escaping, template delimiter detection, and GitHub reference filtering).

@pelikhan
Copy link
Copy Markdown
Collaborator

Tests in this file call sanitizeTitle → sanitizeContentCore, which now
calls core.info/core.debug unconditionally (guards removed). Add
beforeEach/afterEach to set up and tear down a global.core stub,
consistent with sanitize_content.test.cjs.

Agent-Logs-Url: https://github.com/github/gh-aw/sessions/a74f7f25-be32-4dc6-b4e5-48ded4834099

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 27, 2026

@copilot fix tests https://github.com/github/gh-aw/actions/runs/25007075019/job/73233354478

Fixed in b8ae0f0sanitize_title.test.cjs now sets up a global.core stub in beforeEach / afterEach, consistent with sanitize_content.test.cjs. The three failing tests all pass locally now.

@pelikhan pelikhan merged commit a91e3d2 into main Apr 27, 2026
4 of 7 checks passed
@pelikhan pelikhan deleted the copilot/fix-safe-output-sanitizer branch April 27, 2026 17:16
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 27, 2026

Smoke CI completed successfully!

@github-actions
Copy link
Copy Markdown
Contributor

✅ smoke-ci: safeoutputs CLI comment + comment-memory run (25009243057)

Generated by Smoke CI for issue #28736 ·

@github-actions
Copy link
Copy Markdown
Contributor

Comment Memory

CI lights the path\nGreen checks bloom at dawn\nQuiet bots still sing

Note

This comment is managed by comment memory.

It stores persistent context for this thread in the code block at the top of this comment.
Edit only the text inside the backtick fences; workflow metadata and the footer are regenerated automatically.

Learn more about comment memory

Generated by Smoke CI for issue #28736 ·

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.

Safe-output sanitizer escapes template delimiters inside GitHub suggestion blocks

3 participants