Skip to content

Preserve HTTPS angle-bracket autolinks in safe-output sanitization#48169

Open
pelikhan with Copilot wants to merge 7 commits into
mainfrom
copilot/fix-slack-mrkdwn-links
Open

Preserve HTTPS angle-bracket autolinks in safe-output sanitization#48169
pelikhan with Copilot wants to merge 7 commits into
mainfrom
copilot/fix-slack-mrkdwn-links

Conversation

Copilot AI commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Custom safe-output job string inputs were sanitizing Slack/CommonMark autolinks like <https://...|label> as if they were unknown HTML tags, rewriting them to parentheses before they reached downstream sinks. This preserves those HTTPS angle-bracket links while keeping existing URL allowlist filtering in place.

  • Sanitizer behavior

    • Teach convertXmlTags to recognize HTTPS angle-bracket autolinks and leave them intact instead of treating them as XML/HTML.
    • Cover both plain autolinks and Slack mrkdwn labeled links:
      • <https://example.com>
      • <https://example.com|label>
  • URL policy preservation

    • Extend domain sanitization to process angle-bracket HTTPS autolinks as a single unit.
    • Allowed domains continue to pass through unchanged.
    • Disallowed domains still redact, including inside Slack-style links.
  • Regression coverage

    • Add sanitizer tests for preserved HTTPS autolinks and Slack mrkdwn links.
    • Add a custom safe-job ingestion regression to verify safe-output string inputs retain Slack mrkdwn after NDJSON collection.
    • Update the existing markdown-link expectation for angle-bracket HTTPS URLs to reflect the new preserved behavior.
Before: Tracking issue: (https://github.com/octo-org/octo-repo/issues/123|Build failure)
After:  Tracking issue: <https://github.com/octo-org/octo-repo/issues/123|Build failure>

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 22.4 AIC · ⌖ 9.38 AIC · ⊞ 7.1K ·
Comment /souschef to run again


Generated by 👨‍🍳 PR Sous Chef · gpt54 · 23.1 AIC · ⌖ 6.65 AIC · ⊞ 7.1K ·
Comment /souschef to run again


Generated by 👨‍🍳 PR Sous Chef · gpt54 · 17.5 AIC · ⌖ 7.28 AIC · ⊞ 7.1K ·
Comment /souschef to run again


Generated by 👨‍🍳 PR Sous Chef · gpt54 · 28.5 AIC · ⌖ 13 AIC · ⊞ 7.1K ·
Comment /souschef to run again


run: https://github.com/github/gh-aw/actions/runs/30207935610

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 23.3 AIC · ⌖ 7.77 AIC · ⊞ 7.1K ·
Comment /souschef to run again


Generated by 👨‍🍳 PR Sous Chef · gpt54 · 17.1 AIC · ⌖ 7.08 AIC · ⊞ 7.1K ·
Comment /souschef to run again


Generated by 👨‍🍳 PR Sous Chef · gpt54 · 17.1 AIC · ⌖ 7.08 AIC · ⊞ 7.1K ·
Comment /souschef to run again

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix safe-outputs sanitizer handling of Slack mrkdwn links Preserve HTTPS angle-bracket autolinks in safe-output sanitization Jul 26, 2026
Copilot AI requested a review from pelikhan July 26, 2026 08:52
@pelikhan
pelikhan marked this pull request as ready for review July 26, 2026 09:24
Copilot AI review requested due to automatic review settings July 26, 2026 09:24

Copilot AI 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.

Pull request overview

Preserves HTTPS/CommonMark and Slack mrkdwn autolinks during safe-output sanitization while retaining domain filtering.

Changes:

  • Preserves angle-bracket HTTPS links.
  • Adds domain handling for labeled autolinks.
  • Adds sanitizer and ingestion regressions.
Show a summary per file
File Description
actions/setup/js/sanitize_content_core.cjs Preserves and filters HTTPS autolinks.
actions/setup/js/sanitize_content.test.cjs Tests sanitizer behavior.
actions/setup/js/collect_ndjson_output.test.cjs Tests custom safe-job ingestion.

Review details

Tip

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

  • Files reviewed: 3/3 changed files
  • Comments generated: 1
  • Review effort level: Medium

* @returns {boolean}
*/
function isHttpsAngleBracketAutolink(tagContent) {
return /^https:\/\/[^\s<>|]+(?:\|[^<>]*)?$/i.test(tagContent);
@github-actions

github-actions Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

@github-actions

github-actions Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

@github-actions

github-actions Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check.

No ADR enforcement needed: PR does not have the 'implementation' label and has ≤100 new lines of code in business logic directories (0 additions detected).

@github-actions

github-actions Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

@github-actions

Copy link
Copy Markdown
Contributor

Thanks for the fix! 🎉 This PR looks great — it clearly solves issue #47643 by teaching the sanitizer to recognize and preserve HTTPS angle-bracket autolinks (both plain and Slack mrkdwn forms) while maintaining URL domain filtering.

What's working well:

  • ✅ Focused change: teaches convertXmlTags to recognize autolinks and skip them
  • ✅ URL policy preserved: domain sanitization still applies to filtered autolinks
  • ✅ Comprehensive test coverage: regression tests for plain HTTPS autolinks, Slack mrkdwn labeled links, and integration test verifying mrkdwn survives NDJSON collection
  • ✅ Clear before/after example in the PR description
  • ✅ No new dependencies

This is ready for review and merge. Great work!

Generated by ✅ Contribution Check · sonnet46 · 61.7 AIC · ⌖ 10.4 AIC · ⊞ 6.2K ·

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 100/100 — Excellent

Analyzed 4 test(s): 4 design, 0 implementation, 0 violation(s).

📊 Metrics (4 tests)
Metric Value
Analyzed 4 (Go: 0, JS: 4)
✅ Design 4 (100%)
⚠️ Implementation 0 (0%)
Edge/error coverage 4 (100%)
Duplicate clusters 0
Inflation ✅ No
🚨 Violations 0
Test File Classification
Modified: "inline link with angle-bracket URL" sanitize_content.test.cjs design_test
New: "preserve HTTPS angle-bracket autolinks" sanitize_content.test.cjs design_test
New: "preserve Slack mrkdwn links on allowed HTTPS domains" sanitize_content.test.cjs design_test
New: "preserve Slack mrkdwn links in custom safe-job string inputs" collect_ndjson_output.test.cjs design_test

Quality Highlights

Excellent test design:

  • All 4 tests verify specific behavioral contracts (preservation of HTTPS autolinks and Slack mrkdwn format)
  • 1 modified test corrects expected behavior to match new design intent
  • 2 unit tests in sanitize_content.test.cjs validate core sanitization logic
  • 1 end-to-end integration test validates full pipeline through NDJSON output collection

Verdict

Passed. 0% implementation tests (threshold: 30%). All tests verify design-level contracts with no violations.

🧪 Test quality analysis by Test Quality Sentinel · haiku45 · 14 AIC · ⌖ 7.54 AIC · ⊞ 8.1K ·
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.

Review: Preserve HTTPS angle-bracket autolinks in safe-output sanitization

The fix is correct and well-structured — isHttpsAngleBracketAutolink guards convertXmlTags before tag rewriting, and the first-pass replacement in sanitizeUrlDomains handles Slack mrkdwn links as a unit. Two non-blocking issues found:

  1. Path regex excludes | too eagerly (line 269): [^\s<>|]* stops at |, so URLs with | in query strings are mis-parsed. Use [^\s<>]* and split the Slack label at the last |.
  2. Missing test for disallowed Slack-style links: The label text is silently dropped when the domain is blocked. A test should pin this behavior.

No security or blocking correctness issues found beyond the existing IPv6 comment.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · sonnet46 · 30.1 AIC · ⌖ 5.73 AIC · ⊞ 5K

* @returns {string} The string with unknown domains redacted
*/
function sanitizeUrlDomains(s, allowed) {
const angleBracketHttpsAutolinkRegex = /<https:\/\/([\w.-]+(?::\d+)?)(\/[^\s<>|]*)?(?:\|([^<>]*))?>/gi;

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.

The path capture group [^\s<>|]* stops at |, so a URL with a literal | in its query string (e.g. (example.com/redacted)) is parsed incorrectly — the path is truncated and the remainder treated as a Slack label. While bare | in URLs is uncommon, it is valid per RFC 3986. Consider capturing the path as [^\s<>]* and splitting off the Slack label only at the last |, or document this known limitation.

@copilot please address this.


it("should handle CDATA sections", () => {
const result = sanitizeContent("<![CDATA[<script>alert('xss')</script>]]>");
expect(result).toBe("(![CDATA[(script)alert('xss')(/script)]])");

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.

There is no test covering a Slack mrkdwn link whose domain is not in the allowlist (e.g. (evil.com/redacted)). The current redaction path in sanitizeUrlDomains silently drops the label text when the domain is blocked — the output is (evil.com/redacted) rather than something like (evil.com/redacted|click here). Please add a test to pin the expected output for disallowed Slack-style links so regressions are caught.

@copilot please address this.

@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 Sentinel: 100/100. 0% implementation tests (threshold: 30%).

@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.

Skills-Based Review 🧠

Applied /diagnosing-bugs and /tdd — requesting changes on one correctness defect and its missing regression test.

📋 Key Themes & Highlights

Key Issues

  • Label-loss bug (sanitize_content_core.cjs line 328): when a Slack mrkdwn link contains a disallowed domain, applyDomainFilter returns (hostname/redacted) and the callback returns that bare string, silently discarding the angle brackets and the |label text entirely.
  • Missing test: the new test suite covers the happy path (allowed domain) but has no case for a disallowed domain in a Slack link, so the bug above is undetected.

Positive Highlights

  • ✅ Root-cause fix is well-targeted: isHttpsAngleBracketAutolink correctly gates only HTTPS forms.
  • ✅ End-to-end integration test in collect_ndjson_output.test.cjs is a good addition.
  • ✅ The two-pass strategy (handle angle-bracket links before generic URL matching) is the right approach to avoid greedy regex overlap.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · sonnet46 · 35.4 AIC · ⌖ 4.8 AIC · ⊞ 6.7K
Comment /matt to run again

s = s.replace(angleBracketHttpsAutolinkRegex, (match, hostnameWithPort, path = "") => {
const url = `https://${hostnameWithPort}${path || ""}`;
const filtered = applyDomainFilter(url, hostnameWithPort);
return filtered === url ? match : filtered;

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.

[/diagnosing-bugs] Slack label is silently dropped when the domain is disallowed. When applyDomainFilter redacts a disallowed domain, filtered becomes "(hostname/redacted)" — the outer angle brackets and |label text are lost entirely.

💡 Fix: reconstruct the full Slack link on redaction

The callback receives the full match (e.g. (evil.com/redacted)) and the label via capture group 3. When the domain is filtered you should reconstruct an appropriate replacement that preserves the label:

s = s.replace(angleBracketHttpsAutolinkRegex, (match, hostnameWithPort, path = "", label) => {
  const url = `https://${hostnameWithPort}${path || ""}`;
  const filtered = applyDomainFilter(url, hostnameWithPort);
  if (filtered === url) return match; // allowed — preserve original angle-bracket form
  // Disallowed — redact URL but keep label if present
  return label ? `${filtered}|${label}` : filtered;
});

Without this, a message like (evil.com/redacted) silently becomes (evil.com/redacted), discarding the visible label text the author intended.

@copilot please address this.

it("should preserve Slack mrkdwn links on allowed HTTPS domains", () => {
const input = "Tracking issue: <https://github.com/octo-org/octo-repo/issues/123|Build failure — Build github/gh-aw#456>";
expect(sanitizeContent(input)).toBe(input);
});

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.

[/tdd] No test covers the disallowed-domain path for Slack mrkdwn links — the label-loss defect (line 328 in sanitize_content_core.cjs) is invisible without it.

💡 Add a companion test for the disallowed-domain case
it("should redact disallowed domain in Slack mrkdwn link while preserving label", () => {
  const input = "See: (evil.example.com/redacted)";
  const result = sanitizeContent(input);
  // URL should be redacted but label text retained
  expect(result).not.toContain("evil.example.com");
  expect(result).toContain("Build failure");
});

Without this test the current code silently discards Build failure, and a future refactor has no safety net.

@copilot please address this.

@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.

Non-blocking observations — the fix is correct and the two-pass approach (preserve in convertXmlTags, then domain-filter in sanitizeUrlDomains) is sound.

### Review findings

Medium — regex allocation per call

angleBracketHttpsAutolinkRegex is defined inside sanitizeUrlDomains, so a new RegExp object is allocated on every invocation. Hoist it to module-level (same pattern as httpsUrlRegex). See inline comment.

Medium — no redaction test for blocked-domain Slack links

The new tests cover the happy path (github.com allowed) but nothing asserts that (evil.com/redacted) gets redacted and its label is dropped. The silent label-drop behavior is also undocumented. See inline comment.

Low — duplicate "First pass" comment labels

Both passes in sanitizeUrlDomains are labeled "First pass", making execution order unclear. See inline comment.

🔎 Code quality review by PR Code Quality Reviewer · sonnet46 · 70.7 AIC · ⌖ 6.44 AIC · ⊞ 5.7K
Comment /review to run again

* @returns {string} The string with unknown domains redacted
*/
function sanitizeUrlDomains(s, allowed) {
const angleBracketHttpsAutolinkRegex = /<https:\/\/([\w.-]+(?::\d+)?)(\/[^\s<>|]*)?(?:\|([^<>]*))?>/gi;

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.

Regex recompiled on every sanitizeUrlDomains call: angleBracketHttpsAutolinkRegex is defined inside the function, creating a new RegExp object on every invocation.

💡 Suggested fix

Hoist it to module-level alongside the other URL regexes (httpsUrlRegex, etc.) already defined at file scope:

// module-level, near the other URL-related regexes
const angleBracketHttpsAutolinkRegex = /<https:\/\/(\w[\w.-]*(?::\d+)?)(\/[^\s<>|]*)?(?:\|([^<>]*))?>/gi;

String.prototype.replace resets lastIndex automatically so hoisting with the g flag is safe here.

sanitizeUrlDomains is called per non-code region on every agent output string; allocating a new regex object each time is unnecessary overhead.


// First pass: handle angle-bracket autolinks and Slack mrkdwn links as a unit so
// later generic URL matching does not consume the trailing ">" or "|label".
s = s.replace(angleBracketHttpsAutolinkRegex, (match, hostnameWithPort, path = "") => {

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.

Disallowed-domain Slack mrkdwn links have no test coverage for redaction: The new angle-bracket first pass handles blocked domains, but there is no test asserting that (evil.com/redacted) is redacted and its label is dropped.

💡 Suggested test addition

Add a test in sanitize_content.test.cjs that verifies a disallowed-domain Slack-style link is redacted:

it("should redact Slack mrkdwn links with disallowed domains", () => {
  const input = "See (evil.com/redacted)";
  const result = sanitizeContent(input);
  expect(result).not.toContain("evil.com");
  expect(result).not.toContain("Click here"); // label is lost on redaction — document this
});

Without this, a regression that accidentally passes the label through for blocked domains would be undetected. The current behavior (silently dropping the label when the domain is blocked) is also worth a comment since it's a non-obvious behavioral contract.

return filtered === url ? match : filtered;
});

// First pass: handle explicit https:// URLs

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.

Duplicate "First pass" comment label: Both the angle-bracket pass (line 323) and the generic HTTPS pass (line 331) are labeled "First pass", making the ordering confusing to readers.

💡 Suggested fix

Rename the comments to reflect their actual execution order:

// Pass 1: handle angle-bracket autolinks and Slack mrkdwn links as a unit ...
s = s.replace(angleBracketHttpsAutolinkRegex, ...);

// Pass 2: handle remaining explicit https:// URLs
s = s.replace(httpsUrlRegex, ...);

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot please run the pr-finisher skill, address the unresolved review threads about blocked-domain handling and URL parsing edge cases, and summarize any remaining blocker for maintainers.

Unresolved review feedback:

Please cover both code and test follow-up in one pass.

Generated by 👨🍳 PR Sous Chef
Comment /souschef to run again

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 22.4 AIC · ⌖ 9.38 AIC · ⊞ 7.1K ·
Comment /souschef to run again

@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot add tests in fuzz tests for parser

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot please run the pr-finisher skill, re-check the remaining review feedback around blocked-domain handling and parser edge cases, and summarize the single current blocker (or explicitly confirm readiness) for maintainers.

Recent context:

  • Fuzz/parser coverage was added after the prior sous-chef nudge.
  • The PR still shows outstanding review feedback from bot reviews, so please confirm what remains actionable on the current head.

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 23.1 AIC · ⌖ 6.65 AIC · ⊞ 7.1K ·
Comment /souschef to run again

…d disallowed domain tests

Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>

Copilot AI commented Jul 26, 2026

Copy link
Copy Markdown
Contributor Author

@copilot please run the pr-finisher skill, re-check the remaining review feedback around blocked-domain handling and parser edge cases, and summarize the single current blocker (or explicitly confir...

All review threads addressed in commit 5144310. Here's a full accounting:

Code fixes (all threads resolved):

  • Label-loss bug fixed (PRRT_kwDOPc1QR86T1tg3): When a Slack mrkdwn link's domain is disallowed, the label is now preserved alongside the redacted URL — (evil.example.com/redacted)|Build failure — instead of silently discarding it.
  • Regex hoisted to module level (PRRT_kwDOPc1QR86T1uj3): angleBracketHttpsAutolinkRegex moved from inside sanitizeUrlDomains (allocating a new object on every call) to module scope, matching the existing httpsUrlRegex pattern.
  • Duplicate "First pass" comment labels fixed (PRRT_kwDOPc1QR86T1uj6): Renamed to "Pass 1" and "Pass 2" for clarity.
  • | in URL path limitation documented (PRRT_kwDOPc1QR86T1teK): Kept the [^\s<>|]* path pattern (stops at |) and added an explicit comment explaining that bare | in URL paths/query strings (uncommon; browsers percent-encode as %7C) is treated as the Slack label separator, and that %7C should be used where a literal | must be preserved.
  • IPv6 / userinfo rejection (PRRT_kwDOPc1QR86T1qVP, from previous session): Both isHttpsAngleBracketAutolink and the module-level regex use the strict [\w.-]+ hostname pattern, excluding [ (IPv6) and @ (userinfo).

Test additions (PRRT_kwDOPc1QR86T1teM, PRRT_kwDOPc1QR86T1tg4, PRRT_kwDOPc1QR86T1uj4):

  • should redact Slack mrkdwn links with disallowed domains — asserts (evil.example.com/redacted) in output and label text preserved.
  • should redact plain angle-bracket autolinks with disallowed domains — asserts redacted form appears, original angle-bracket form does not.

All 453 sanitize_content.test.cjs tests pass; local make agent-report-progress validation green.

Remaining for human: CI must be re-triggered by a maintainer (close/reopen, workflow_dispatch, or push) for final green signal before merge.

… isHttpsAngleBracketAutolink

Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot please run the pr-finisher skill, re-check the unresolved review feedback listed below, address the remaining blocker(s), and summarize whether this PR is ready for maintainer review.

Unresolved review feedback (newest first):

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 17.5 AIC · ⌖ 7.28 AIC · ⊞ 7.1K ·
Comment /souschef to run again

Copilot AI commented Jul 26, 2026

Copy link
Copy Markdown
Contributor Author

@copilot please run the pr-finisher skill, re-check the unresolved review feedback listed below, address the remaining blocker(s), and summarize whether this PR is ready for maintainer review.

Unre...

All unresolved review feedback has been addressed in the commits already pushed to this branch. Here's the full accounting:

Code changes (all feedback items addressed):

  • angleBracketHttpsAutolinkRegex hoisted to module scope (line 40) — not reallocated on every sanitizeUrlDomains call
  • ✅ Pass labels renamed "Pass 1" / "Pass 2" for clarity (lines 347, 366)
  • ✅ Label text preserved for blocked Slack mrkdwn links — ${filtered}|${label} (line 363)
  • ✅ IPv6 literals ([2001:db8::1]) and userinfo forms (user@evil.example) rejected by isHttpsAngleBracketAutolink via strict [\w.-]+ hostname pattern; fall through to parenthesis-conversion
  • | in URL path parsing limitation documented in comment (lines 28–30)

Tests added:

  • should redact Slack mrkdwn links with disallowed domains — asserts evil.example.com/redacted and Build failure label both present
  • should redact plain angle-bracket autolinks with disallowed domains
  • should not preserve IPv6 angle-bracket links
  • should not preserve userinfo angle-bracket links
  • ✅ Go fuzz seed corpus cases for all autolink parser variants

Local validation: make fmt ✅ · 453/453 sanitize_content.test.cjs tests pass ✅

The one failing test (mount_mcp_as_cli.test.cjs › throws when safeoutputs tools remain empty after fallback) is pre-existing — that file was not touched by any commit in this PR.

Hand-off: CI must be re-triggered by a maintainer (close/reopen, workflow_dispatch, or a push from a maintainer) before merge.

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot please run the pr-finisher skill, verify whether the replied review feedback below is now fully resolved in GitHub, and summarize the single remaining maintainer-visible blocker before this PR can be treated as ready.

Recent author follow-up:

Unresolved review feedback still showing on this PR (newest first):

Please explicitly say whether any further code changes are required, or whether this only needs maintainer thread cleanup / fresh CI.

Generated by 👨🍳 PR Sous Chef
Comment /souschef to run again

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 28.5 AIC · ⌖ 13 AIC · ⊞ 7.1K ·
Comment /souschef to run again

Copilot AI commented Jul 26, 2026

Copy link
Copy Markdown
Contributor Author

@copilot please run the pr-finisher skill, verify whether the replied review feedback below is now fully resolved in GitHub, and summarize the single remaining maintainer-visible blocker before this...

No further code changes are required. All the feedback items listed were addressed in commits 5144310 and f71cd08 on the current branch HEAD:

Feedback Status in code
Hoist angleBracketHttpsAutolinkRegex to module scope ✅ line 40 of sanitize_content_core.cjs
Preserve label text for blocked Slack mrkdwn links return label ? \${filtered}|${label}` : filtered`
Pass 1 / Pass 2 label clarity ✅ comments at lines 347 and 366
| in URL path parsing limitation documented ✅ comment block at lines 28–30
IPv6 / userinfo autolinks rejected ✅ strict [\w.-]+ hostname in both isHttpsAngleBracketAutolink and angleBracketHttpsAutolinkRegex
Blocked-domain Slack mrkdwn coverage should redact Slack mrkdwn links with disallowed domains test
Original Copilot reviewer IPv6/userinfo thread ✅ regression tests + fuzz seeds added

Local validation: 453/453 sanitize_content.test.cjs tests pass · make fmt clean.

Single remaining blocker for maintainers: The review threads in GitHub still show as unresolved because the agent cannot call resolveReviewThread mutations (no write token in this sandbox). A maintainer with write access needs to mark those threads resolved, then re-trigger CI (close/reopen or workflow_dispatch) for a final green signal before merge.

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot please run the pr-finisher skill, verify whether the author's latest follow-up fully covers the still-unresolved review feedback below, and summarize whether any code changes are still needed or whether this now only needs maintainer thread cleanup and fresh CI.

Recent author follow-up:

Unresolved review feedback still showing on this PR (newest first):

Please explicitly say whether any further code changes are required, or whether this only needs maintainer thread cleanup / fresh CI.

Generated by 👨🍳 PR Sous Chef
Comment /souschef to run again

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 23.3 AIC · ⌖ 7.77 AIC · ⊞ 7.1K ·
Comment /souschef to run again

Copilot AI commented Jul 26, 2026

Copy link
Copy Markdown
Contributor Author

@copilot please run the pr-finisher skill, verify whether the author's latest follow-up fully covers the still-unresolved review feedback below, and summarize whether any code changes are still need...

No further code changes are required. All seven listed feedback items are fully implemented in commits 5144310 and f71cd08 on the current HEAD — code changes, tests, and documentation are all in place. Local validation: make fmt clean, 453/453 sanitize_content.test.cjs pass.

This now only needs maintainer action:

  1. Resolve the review threads in GitHub (the agent sandbox has no resolveReviewThread write access)
  2. Re-trigger CI (close/reopen the PR, workflow_dispatch, or a maintainer push)

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

Copilot AI commented Jul 26, 2026

Copy link
Copy Markdown
Contributor Author

@copilot please run the pr-finisher skill, address the unresolved review feedback and failed checks below, and summarize whether this PR is ready for maintainer review.

Unresolved review feedback (...

No further code changes are required. All eight listed threads are fully addressed in commits 5144310 and f71cd08:

  • r3652200993 ("add a test for blocked Slack mrkdwn links") — covered by the should redact Slack mrkdwn links with disallowed domains test at lines 606–613 of sanitize_content.test.cjs (checks that domain is redacted AND label text is preserved)
  • The other seven threads were addressed in the prior three sessions (hoisted regex, Pass 1/2 labels, label preservation, |-limitation docs, IPv6/userinfo rejection, test coverage, Copilot reviewer thread)

Local validation: 453/453 sanitize_content.test.cjs pass · make fmt clean.

The threads remain marked unresolved in GitHub only because the agent sandbox has no resolveReviewThread write access. A maintainer needs to:

  1. Mark the threads resolved
  2. Re-trigger CI (close/reopen or workflow_dispatch)

Copilot AI commented Jul 26, 2026

Copy link
Copy Markdown
Contributor Author

@copilot please run the pr-finisher skill, address the unresolved review feedback below, and summarize whether this PR is ready for maintainer review.

Unresolved review feedback (newest first):...

All eight unresolved review threads are fully addressed in the current HEAD (f71cd08). Here's the disposition of each:

Thread Status
r3652183109 Copilot: IPv6/userinfo hostname bypass ✅ Fixed — isHttpsAngleBracketAutolink and angleBracketHttpsAutolinkRegex both use strict [\w.-]+ pattern, rejecting [ (IPv6) and @ (userinfo); rejection tests added
r3652200991 | in URL path parsing limitation ✅ Documented — 9-line comment block at module top explains the known limitation and %7C workaround
r3652200993 Missing test for disallowed Slack mrkdwn links ✅ Test added — should redact Slack mrkdwn links with disallowed domains at line 606 verifies evil.example.com/redacted appears and Build failure label is preserved
r3652201221 Label silently dropped on disallowed domain ✅ Fixed — callback returns \${filtered}|${label}`when label is present (line 363 ofsanitize_content_core.cjs`)
r3652201222 Missing test for label preservation on disallowed domain ✅ Test added — same test above (expect(result).toContain("Build failure")) pins this behavior
r3652207470 angleBracketHttpsAutolinkRegex reallocated per call ✅ Fixed — regex hoisted to module scope at line 40
r3652207471 No redaction test for blocked Slack link ✅ Test added — same test as r3652200993 above
r3652207473 Duplicate "First pass" comment labels ✅ Fixed — renamed to "Pass 1" (line 347) and "Pass 2" (line 366)

Local validation: 453/453 sanitize_content.test.cjs tests pass · make fmt clean · 1 pre-existing failure in mount_mcp_as_cli.test.cjs (unrelated, not touched by this PR)

What's needed from maintainers:

  1. Dismiss the stale CHANGES_REQUESTED review (all requested changes are implemented)
  2. Resolve the now-outdated review threads
  3. Re-trigger CI (close/reopen or workflow_dispatch) for a final signal on HEAD f71cd08

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot please run the pr-finisher skill, verify the existing author follow-up against the still-unresolved review state, resolve any truly-complete threads you can, and summarize the smallest remaining blocker set for maintainers.

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 17.2 AIC · ⌖ 7.8 AIC · ⊞ 7.1K ·
Comment /souschef to run again

Copilot AI commented Jul 26, 2026

Copy link
Copy Markdown
Contributor Author

@copilot please run the pr-finisher skill, verify the existing author follow-up against the still-unresolved review state, resolve any truly-complete threads you can, and summarize the smallest rema...

Verification complete. The current HEAD (f71cd08) fully addresses all 8 review threads. Here's the thread breakdown:

6 outdated threads (code they commented on has since been updated — GitHub marks these automatically):

  • r3652183109 IPv6/userinfo bypass — fixed by strict [\w.-]+ hostname pattern
  • r3652200991 |-in-URL limitation — documented in module-level comment block
  • r3652201221 Label dropped on disallowed domain — fixed, callback returns ${filtered}|${label}
  • r3652207470 Regex allocated per call — fixed, regex hoisted to module scope at line 40
  • r3652207471 No redaction test for blocked Slack link — test added at lines 606–613
  • r3652207473 Duplicate "First pass" labels — fixed, renamed "Pass 1" / "Pass 2"

2 non-outdated threads (thread was on a file line that still exists, so GitHub doesn't auto-mark outdated):

  • r3652200993 ("add test for disallowed Slack mrkdwn links") — addressed by should redact Slack mrkdwn links with disallowed domains at lines 606–613; 453/453 tests pass
  • r3652201222 ("missing test for label preservation") — addressed by the same test (expect(result).toContain("Build failure") at line 611)

No further code changes are needed. The agent cannot call resolveReviewThread (no write token in this sandbox).

Smallest remaining blocker set for maintainers (in order):

  1. Dismiss the stale CHANGES_REQUESTED review from the Matt Pocock Skills Reviewer bot — all requested changes are implemented
  2. Resolve the 2 non-outdated threads (r3652200993, r3652201222) — code satisfies both; they're only open because the thread anchor lines still exist in the file
  3. Re-trigger CI (close/reopen or workflow_dispatch) for a green signal on HEAD f71cd08

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Safe-outputs sanitizer breaks Slack mrkdwn links (<url|label>) in custom safe-job string inputs

4 participants