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


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


Run: https://github.com/github/gh-aw/actions/runs/30218624846

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 19 AIC · ⌖ 8.59 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

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

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot please run the pr-finisher skill, review the current branch state, address any still-open reviewer feedback and failed checks below, and reply with whether this PR is ready for maintainer review.

Recent review signals to verify:

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 14.5 AIC · ⌖ 7.42 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, review the current branch state, address any still-open reviewer feedback and failed checks below, and reply with whether this PR is ready for maintainer r...

Current state verified:

Reviews — The 4 referenced review signals (kwDOPc1QR88...) aren't yet visible in the API; they are likely from the check run that started at 18:19 and may still be processing. All currently-visible reviews are based on the original commit b965f41. Every issue those reviews identified has been fixed in the current HEAD (f71cd08):

  • CHANGES_REQUESTED (Matt Pocock Skills Reviewer): both issues it flagged (label-loss bug + missing test) are fixed
  • All COMMENTED reviews: all inline suggestions implemented (regex hoisted, Pass 1/2 labels, |-limitation documented, IPv6/userinfo rejected, disallowed-domain tests added)
  • APPROVED (Test Quality Sentinel): still valid

Checks — All 6 completed CI runs show success. One run is in-progress (started 18:19); no failures to address.

Mergeableblocked only due to the stale CHANGES_REQUESTED review, not due to any code issue.

No further code changes are needed. Ready for maintainer action:

  1. Dismiss the stale CHANGES_REQUESTED review (Matt Pocock Skills Reviewer) — all its requested changes are in HEAD
  2. Re-trigger CI after dismissal for a green signal on current HEAD

@github-actions

Copy link
Copy Markdown
Contributor

🤖 PR Triage — §30214847023

Field Value
Category bug
Risk 🟢 Low
Priority 68/100
Breakdown Impact 28 · Urgency 22 · Quality 18
Action fast_track

Rationale: HTTPS angle-bracket autolink preservation in safe-output sanitization. Low-risk: 139 additions focused on test files + sanitize_content_core.cjs. CI passing (copilot: success). Multiple AI reviewers approved. Well-tested with fuzz test added. Ready for expedited human review.

Generated by 🔧 PR Triage Agent · sonnet46 · 34.1 AIC · ⌖ 7.94 AIC · ⊞ 5.7K ·

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot please run the skill, review the current branch state, address any still-open reviewer feedback and failed checks, and reply with whether this PR is ready for maintainer review.

Run: https://github.com/github/gh-aw/actions/runs/30215321924

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 16.9 AIC · ⌖ 7.13 AIC · ⊞ 5.6K ·
Comment /souschef to run again

Copilot AI commented Jul 26, 2026

Copy link
Copy Markdown
Contributor Author

@copilot please run the skill, review the current branch state, address any still-open reviewer feedback and failed checks, and reply with whether this PR is ready for maintainer review.

Run: https:...

Current branch state verified — no further code changes are required.

Local validation:

  • make agent-report-progress-no-test — all 262 workflows compile, lock files in sync, lint clean
  • ✅ 516/516 JS tests pass (sanitize_content.test.cjs 453 + sanitize_content_core_parser.test.cjs 63)

Review threads: All 8 threads are addressed in commits 5144310 and f71cd08:

Thread Status in HEAD
r3652183109 IPv6/userinfo bypass [\w.-]+ hostname pattern rejects [ and @; rejection tests added
r3652200991 | in URL path limitation ✅ Documented in module-level comment block
r3652200993 Missing test for disallowed Slack mrkdwn should redact Slack mrkdwn links with disallowed domains test at line 606
r3652201221 Label dropped on disallowed domain ✅ Callback returns `` `${filtered}
r3652201222 Missing label-preservation test ✅ Same test asserts expect(result).toContain("Build failure")
r3652207470 Regex allocated per call angleBracketHttpsAutolinkRegex hoisted to module scope at line 40
r3652207471 No redaction test for blocked Slack link ✅ Same test as r3652200993
r3652207473 Duplicate "First pass" comments ✅ Renamed to "Pass 1" / "Pass 2" at lines 347/366

PR status:

  • ✅ Reviews (code) — all requested changes implemented
  • ✅ Checks (local) — all tests pass
  • ❓ Checks (CI) — stale after agent push; needs human re-trigger
  • ❌ Mergeable — blocked by stale CHANGES_REQUESTED review from Matt Pocock Skills Reviewer bot (all its requests are now implemented)

Hand-off for maintainers:

  1. Dismiss the stale CHANGES_REQUESTED review — all its requested changes are in HEAD f71cd08
  2. Resolve the 2 non-outdated threads (r3652200993, r3652201222) — satisfied by existing tests
  3. Re-trigger CI (close/reopen or workflow_dispatch) for a green signal on current HEAD

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

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

Unresolved review threads (newest first):

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 19 AIC · ⌖ 8.59 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, review the current branch state, address the unresolved feedback below, and reply with whether this PR is ready for maintainer review.

Unresolved review t...

No further code changes are required. All 8 listed threads are addressed in the current HEAD (f71cd08):

Thread Disposition
r3652183109 IPv6/userinfo bypass OutdatedisHttpsAngleBracketAutolink uses strict [\w.-]+; rejection tests added
r3652200991 |-in-URL limitation Outdated — documented in the module-level comment block
r3652200993 Missing test for disallowed Slack mrkdwn ✅ Test should redact Slack mrkdwn links with disallowed domains (line 606) covers this
r3652201221 Label dropped on disallowed domain Outdated — callback returns `` `${filtered}
r3652201222 Missing label-preservation test ✅ Same test asserts expect(result).toContain("Build failure")
r3652207470 Regex allocated per call OutdatedangleBracketHttpsAutolinkRegex hoisted to module scope at line 40
r3652207471 No redaction test for blocked Slack link Outdated — same test as r3652200993 above
r3652207473 Duplicate "First pass" labels Outdated — renamed to "Pass 1" / "Pass 2" at lines 347/366

6 threads are is_outdated: true (GitHub auto-marks these when the diff lines change). The 2 non-outdated threads (r3652200993, r3652201222) are satisfied by existing tests; they remain unresolved only because the agent sandbox has no resolveReviewThread write access.

Remaining for maintainers:

  1. Dismiss the stale CHANGES_REQUESTED review (Matt Pocock Skills Reviewer) — all requests implemented
  2. Resolve the 2 non-outdated threads (r3652200993, r3652201222)
  3. Re-trigger CI (close/reopen or workflow_dispatch) for a green signal on HEAD f71cd08

@github-actions

Copy link
Copy Markdown
Contributor

🤖 PR Triage

Field Value
Category bug
Risk 🟢 low
Priority Score 45 / 100
Score Breakdown Impact: 20 · Urgency: 15 · Quality: 10
Recommended Action ⚡ fast_track

Summary: Safe-output sanitizer incorrectly rewrites Slack/CommonMark HTTPS autolinks <(redacted)|label> breaking downstream sinks. Low risk, isolated regex change with existing test coverage. CI passes (copilot ✅). Most recent review: CHANGES_REQUESTED from github-actions[bot] — verify those are addressed.

Next action: Resolve any outstanding review comments, then fast-track.

Generated by 🔧 PR Triage Agent · sonnet46 · 49 AIC · ⌖ 8.25 AIC · ⊞ 5.7K ·

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